This is an R Markdown Notebook for analysis using data on the DC Bus System (WMATA Metrobus). The data were obtained here:

https://planitmetro.com/2016/11/16/data-download-metrobus-vehicle-location-data/

Load the packages to be used.

Get the Bus data.

First let’s check the working directory.


getwd()

Then, actually get the data.

Put the daily data together.


AllDays <- bind_rows(list(Oct03Raw, Oct04Raw, Oct05Raw, Oct06Raw, Oct07Raw),
                     .id = c("group")
                    )
# dim(AllDays)
str(AllDays)

Deleting old data frames.


for (i in 3:7){
  rm(list = ls(pattern = paste0("Oct0", i, "Raw")
              )
    )
  
  message("Deleting Oct0", i, "Raw")
  }

Updating variable types.

Then, sorting the data and adding a RowNumber (to be used for identifying rows later in the analyses.)


rm(i)


AllDays$group <- factor(AllDays$group)
AllDays$Route_Direction <- factor(AllDays$Route_Direction)
AllDays$Event_Time <- as.POSIXct(AllDays$Event_Time, format = "%m-%d-%y %I:%M:%S %p")
AllDays$Departure_Time <- as.POSIXct(AllDays$Departure_Time, format = "%m-%d-%y %I:%M:%S %p")

str(AllDays)


AllDays_Sorted <- arrange(AllDays,
                          Bus_ID,
                          Event_Time
                         ) %>% 
  mutate(RowNum_OG = row_number() # this is useful in identify the row later on
        )

rm(AllDays)
str(AllDays_Sorted)

# View(head(AllDays_Sorted, 100))

Inspecting the values of Stop_ID, and finding that it can take the values “” (blank) and “NULL”.


View(group_by(AllDays_Sorted,
              Stop_ID
             ) %>% 
       summarise(
         Cnt = n()
         ) %>% 
       arrange(Stop_ID)
    )

View(filter(AllDays_Sorted,
            is.na(Stop_ID) |
              Stop_ID == "" |
              Stop_ID == "NULL"
           ) %>% 
       arrange(Stop_Desc)
    )

Creating a table of distinct Stop_Desc values when Stop_ID is “” (blank) or “NULL”.


StopID_New <- filter(AllDays_Sorted,
                     is.na(Stop_ID) |
                       Stop_ID == "" |
                       Stop_ID == "NULL"
                    ) %>% 
  select(Stop_ID, Stop_Desc) %>% 
  distinct() %>% 
  arrange(Stop_ID, Stop_Desc) %>% 
  mutate(StopID_New = 1:nrow(.)
        )

View(StopID_New)
StopID_New

Creating a full updated table by filling in StopID_New for when Stop_ID is “” (blank) or NULL.


AllDays_StopIDNew <- left_join(AllDays_Sorted,
                               select(StopID_New,
                                      Stop_Desc,
                                      StopID_New
                                     ),
                               by = c("Stop_Desc" = "Stop_Desc")
                              ) %>% 
  mutate(StopID_Clean = ifelse(is.na(StopID_New),
                               Stop_ID,
                               StopID_New
                              ),
         StopID_Indicator = factor(ifelse(is.na(StopID_New),
                                          "ID_OK",
                                          "ID_Bad"
                                         )
                                  )
        )

rm(StopID_New)
rm(AllDays_Sorted)
str(AllDays_StopIDNew)

# View(tail(AllDays_StopIDNew, 500))
# View(filter(AllDays_StopIDNew,
#             Stop_Desc == "METROWAY ANNNOUCEMNT CORR"
#            )
#     )

Lat Long stats for pulling in Zip codes later.


LL_Stats <- group_by(AllDays_StopIDNew,
                     StopID_Clean
                    ) %>% 
  summarise(Lat_Mean = mean(Latitude, na.rm = TRUE),
            Lat_Med = median(Latitude, na.rm = TRUE),
            Lng_Mean = mean(Longitude, na.rm = TRUE),
            Lng_Med = median(Longitude, na.rm = TRUE)
           ) %>% 
  mutate(Lat_MeaLessMed = Lat_Mean - Lat_Med,
         Lng_MeaLessMed = Lng_Mean - Lng_Med,
         RowNum = row_number()
        )

str(LL_Stats)
summary(LL_Stats)

View(head(arrange(LL_Stats,
                  Lat_MeaLessMed
                 ),
          500
         )
    )

View(head(arrange(LL_Stats,
                  desc(Lat_MeaLessMed)
                 ),
          500
         )
    )

View(head(arrange(LL_Stats,
                  Lng_MeaLessMed
                 ),
          500
         )
    )

View(head(arrange(LL_Stats,
                  desc(Lng_MeaLessMed)
                 ),
          500
         )
    )

Pulling in Zip Code data from api.geonames.org.


# URL EXAMPLE:
# http://api.geonames.org/findNearbyPostalCodesJSON?lat=38.89560&lng=-76.94873&radius=0&username=supermdat

url_1 <- "http://api.geonames.org/findNearbyPostalCodesJSON?lat="
url_2 <- "&lng="
url_3 <- "&radius=0&username="
username <- "supermdat"


# need to group in bunches as http://api.geonames.org limits pulls to 2000 per hour


##### Store everything in multiple lists
pages1 <- list()


system.time(

for(i in 1:1000){
  lat <- filter(LL_Stats,
                RowNum == i
               ) %>%
    select(Lat_Med)
  
  lng <- filter(LL_Stats,
                RowNum == i
               ) %>%
    select(Lng_Med)
  
  APIData1 <- fromJSON(paste0(url_1,
                              lat,
                              url_2,
                              lng,
                              url_3,
                              username
                             ),
                       flatten = TRUE
                      )
  
  message("Retrieving Zip Code ", i)
  
  pages1[[i]] <- APIData1$postalCodes
  
}
)


##### Combine the lists into one page
Zips1 <- rbind.pages(pages1[sapply(pages1, length) > 0])


##### Combine all pages
Zips_All <- bind_rows(Zips0,
                      Zips1,
                      Zips2,
                      Zips3,
                      Zips4,
                      Zips5,
                      Zips6,
                      Zips7,
                      Zips8,
                      Zips9,
                      Zips10,
                      .id = "id"
                     ) %>% 
  mutate(UniqueLatLng = paste(lat, lng, sep = "__")
        )

# str(Zips_All)
# View(head(Zips_All))


# str(LL_Stats)
LL_Stats_UnqLatLng <- mutate(LL_Stats,
                             UniqueLatLng = paste(Lat_Med, Lng_Med, sep = "__")
                            )

# str(LL_Stats_UnqLatLng)
# View(head(LL_Stats_UnqLatLng))


LL_StatsZips <- left_join(LL_Stats_UnqLatLng,
                          Zips_All,
                          by = c("UniqueLatLng" = "UniqueLatLng")
                         )

str(LL_StatsZips)
# View(head(LL_StatsZips))

# Not sure whey these couldn't be found (why they're NA)
View(filter(LL_StatsZips,
            is.na(postalCode)
           )
    )

Join to create one dataset that also includes Zip variables.


rm(url_1, url_2, url_3, username, pages0, pages1, pages2, pages3, pages4, pages5, pages6, pages7, pages8, pages9, pages10, i, lat, lng, APIData0, APIData1, APIData2, APIData3, APIData4, APIData5, APIData6, APIData7, APIData8, APIData9, APIData10, LL_Stats, LL_Stats_UnqLatLng)


AllDays_Zips <- left_join(AllDays_StopIDNew,
                          LL_StatsZips,
                          by = c("StopID_Clean" = "StopID_Clean")
                         ) %>% 
  rename(Stop_State = adminCode1,
         Stop_County = adminName2,
         Stop_City = placeName,
         Stop_Zip = postalCode
         )

rm(AllDays_StopIDNew, LL_StatsZips)
str(AllDays_Zips)

Updating variable types.


AllDays_Zips$Stop_State <- factor(AllDays_Zips$Stop_State)
AllDays_Zips$Stop_County <- factor(AllDays_Zips$Stop_County)
AllDays_Zips$Stop_Zip <- factor(AllDays_Zips$Stop_Zip)
AllDays_Zips$Stop_City <- factor(AllDays_Zips$Stop_City)

AllDays_Zips$distance <- as.numeric(AllDays_Zips$distance)
AllDays_Zips$countryCode <- factor(AllDays_Zips$countryCode)
AllDays_Zips$adminName1 <- factor(AllDays_Zips$adminName1)

str(AllDays_Zips)

Feature engineering.

Inspecting incidences of consecutive Stop_IDs. This is done because investigation showed that many conseutive events occurr at the same Stop_ID, but with various Dwell_Times, Odometer_Distances, etc. All of which affect calculations and analyses.

Create data on the runs (consecutive Stop_IDs).


StopID_Runs <- rle(AllDays_Zips$StopID_Clean)

StopID_Runs$ends <- cumsum(StopID_Runs$lengths)

StopID_Runs$starts <- ifelse(is.na(lag(StopID_Runs$ends)
                                  ),
                             1,
                             lag(StopID_Runs$ends) + 1
                            )

str(StopID_Runs)
# class(StopID_Runs)
# 
# StopID_Runs_df <- data.frame(unclass(StopID_Runs))
# str(StopID_Runs_df)
# class(StopID_Runs_df)
# rm(StopID_Runs_df)

Trying to link data on RunsGroups with the original data (AllDays_Sorted). The goal is to select only one record per RunsGroup - that being the record with the longest Dwell_Time.

I attempted this computation using both data.frames (dplyr) and data.tables (data.table). However, with 2,809,062 rows in one dataset and 3,119,443 rows in the other dataset, the current computation time is over 5 days…so I’m trying a different strategy to only select the first record in a run.


# Create a RunsGroup variable for each run
# StopID_Runs_df$RunsGroup <- paste0("g", seq(1:nrow(StopID_Runs_df)
#                                            )
#                                   )
# 
# str(StopID_Runs_df)
# head(StopID_Runs_df, 25)
# tail(StopID_Runs_df, 25)
# 
# StopID_Runs_df <- StopID_Runs_df %>% 
#   mutate(RowNum = row_number()
#         )
# 
# str(StopID_Runs_df)
# head(StopID_Runs_df, 25)
# tail(StopID_Runs_df, 25)
# 
# 
# # Converting to data.tables for, hopefully, improved performance (speed) in computation
# StopID_Runs_dt <- data.table(StopID_Runs_df)
# setkey(StopID_Runs_dt, RowNum)
# str(StopID_Runs_dt)
# 
# AllDays_Sorted_dt <- data.table(AllDays_Sorted)
# setkey(AllDays_Sorted_dt, RowNum_OG)
# str(AllDays_Sorted_dt)
# # rm(AllDays_Sorted_dt)
# 
# 
# # Actual loop to perform the computations and link to original data (AllDays_Sorted_dt)
# GroupData <- list()
# for(i in 1:nrow(StopID_Runs_dt)
#    ) {
#   assign(paste0("group_", i),
#            StopID_Runs_dt[RowNum == i, RunsGroup]
#           )
# 
#     #####  The code below is the same code as above, but done with dplyr  #####
# 
#     # assign(paste0("group_", i),
#   #        filter(StopID_Runs_df,
#   #               RowNum == i
#   #              ) %>% 
#   #          select(RunsGroup)
#   #       )
# 
#   assign(paste0("group_", i, "_start"),
#          StopID_Runs_dt[RowNum == i, starts]
#         )
# 
#   assign(paste0("group_", i, "_end"),
#          StopID_Runs_dt[RowNum == i, ends]
#         )
# 
#   assign(paste0("group_", i, "_rows"),
#          AllDays_Sorted_dt[RowNum_OG >= as.numeric(get(paste0("group_", i, "_start")
#                                                       )
#                                                   ) &
#                            RowNum_OG <= as.numeric(get(paste0("group_", i, "_end")
#                                                       )
#                                                   ),
#                            RunsGroup := as.character(get(paste0("group_", i)
#                                                         )
#                                                     )
#                           ]
# 
#     #####  The code below is the same as the code above, but done with dplyr  #####
# 
#          # filter(AllDays_Sorted,
#          #        between(RowNum_OG,
#          #                as.numeric(get(paste0("group_", i, "_start")
#          #                              )
#          #                          ),
#          #                as.numeric(get(paste0("group_", i, "_end")
#          #                              )
#          #                          )
#          #               )
#          #       ) %>% 
#          #   mutate(RunsGroup = as.character(get(paste0("group_", i)
#          #                                     )
#          #                                 )
#          #        )
#         )
# 
#   GroupData[[i]] <- get(paste0("group_", i, "_rows"))
# 
#   message("Processing Group ", i, " of 2,809,062")
# }
# 
# 
# GroupData_df <- rbind.fill(GroupData)
# str(GroupData_df)
# head(GroupData_df)
# tail(GroupData_df)
# # rm(GroupData_df)
# 
# 
# group_1
# group_1_start
# group_1_end
# group_1_rows
# group_2_rows
# group_3_rows
# group_50_rows
# str(group_50_rows)
# group_2809062_rows
# GroupData[[1]]
# GroupData[[50]]
# 
# 
# #####  Testing Area (Below)  #####
# #####  Testing Area (Below)  #####
# #####  Testing Area (Below)  #####
# 
# # head(StopID_Runs$starts, 20)
# # head(AllDays_NewOrder$Stop_ID, 20)
# # 
# # 
# # dat <- as.data.frame(c(1,1,7,7,7,9,6,8,2,2,2,1,1,1,1,1))
# # colnames(dat)[1] <- "dat"
# # r <- rle(dat$dat)
# # dat$run <- rep(r$lengths, r$lengths)
# # dat$runLag <- lag(dat$run)
# # dat$cond <- rep(r$values, r$lengths)
# # dat
# # View(dat)

When consecutive Stop_ID occurrs, only take the first occurrence. This is done because the computation time to select only the record with the longest Dwell_Time for each run was too long (over 5 days).

This is probably less than ideal with regards to Dwell_Time, but should not make much difference for calculations of travel time, speed, etc.


AllDays_FirstStopID <- AllDays_Zips[StopID_Runs$starts, ]

dim(AllDays_Zips)
dim(AllDays_FirstStopID)

nrow(AllDays_Zips) - nrow(AllDays_FirstStopID)

rm(AllDays_Zips, StopID_Runs)
str(AllDays_FirstStopID)

Feature engineering.

Creating new variables.


AllDays_AddVars <- mutate(AllDays_FirstStopID,
                          Odometer_Distance_Mi = Odometer_Distance / 5280, #5,280 feet in 1 mile
                          Dwell_Time2 = as.numeric(Departure_Time - Event_Time),
                          Event_Time_Yr = as.integer(year(Event_Time)),
                          Event_Time_Mth = as.integer(month(Event_Time)),
                          Event_Time_Date = day(Event_Time),
                          Event_Time_Day = wday(Event_Time, label = TRUE),
                          Event_Time_Hr = hour(Event_Time),
                          Event_Time_Min = minute(Event_Time),
                          Event_Time_HrGroup = factor(ifelse(Event_Time_Hr < 3,
                                                             "Group0_2",
                                                      ifelse(Event_Time_Hr < 6,
                                                             "Group3_5",
                                                      ifelse(Event_Time_Hr < 9,
                                                             "Group6_8",
                                                      ifelse(Event_Time_Hr < 12,
                                                             "Group9_11",
                                                      ifelse(Event_Time_Hr < 15,
                                                             "Group12_14",
                                                      ifelse(Event_Time_Hr < 18,
                                                             "Group15_17",
                                                      ifelse(Event_Time_Hr < 21,
                                                             "Group18_20",
                                                      ifelse(Event_Time_Hr < 24,
                                                             "Group21_23"
                                                            )))))))),
                                                         levels = c("Group0_2",
                                                                    "Group3_5",
                                                                    "Group6_8",
                                                                    "Group9_11",
                                                                    "Group12_14",
                                                                    "Group15_17",
                                                                    "Group18_20",
                                                                    "Group21_23"
                                                                   ),
                                                         ordered = TRUE
                                                     )
                         )

rm(AllDays_FirstStopID)
str(AllDays_AddVars)

Function for calculating the distance traveled based on the Haversine formula. Original code from: https://www.r-bloggers.com/great-circle-distance-calculations-in-r/


# Calculates the geodesic distance between two points specified by radian latitude/longitude using the Haversine formula (hf)
# gcd.hf <- function(long1, lat1, long2, lat2) {
#   R <- 6371 # Earth mean radius [km]
#   delta.long <- (long2 - long1)
#   delta.lat <- (lat2 - lat1)
#   a <- sin(delta.lat/2)^2 + cos(lat1) * cos(lat2) * sin(delta.long/2)^2
#   c <- 2 * asin(min(1,sqrt(a)))
#   d = R * c * 0.621371 # 1 km = 0.621371 miles
#   return(d) # Distance in miles
# }

Feature engineering.

Creating more variables. Creating a BusEvent row number for future identification purposes. Then, creating various variables to analyze distance traveled and speed.


AllDays_BusDay <- group_by(AllDays_AddVars,
                           Bus_ID,
                           Event_Time_Date
                          ) %>% 
  mutate(BusDay_EventNum = row_number(),  # used to identify Bus movements on a particular date
         
         Route_Lag1 = lag(Route),  # used in future analyses to identify Route changes
         RouteAlt_Lag1 = lag(RouteAlt),  # used in future analyses to identify RouteAlt (direction) changes
         
         Odometer_Distance_Lag1 = lag(Odometer_Distance),
         
         Latitude_L1 = lag(Latitude),
         Longitude_L1 = lag(Longitude),
         # Lat_Radian = Latitude*pi/180,
         # Long_Radian = Longitude*pi/180,
         # Lat_Radian_L1 = lag(Lat_Radian),
         # Long_Radian_L1 = lag(Long_Radian),
         
         # accounting for potential negative distances
         TravelDistance_Ft = ifelse(Odometer_Distance > Odometer_Distance_Lag1,
                                    Odometer_Distance - Odometer_Distance_Lag1,
                                    NA
                                   ),
         TravelDistance_Mi = TravelDistance_Ft / 5280, #5,280 feet in 1 mile
         
         # TravelDistance_Mi2 = gcd.hf(long1 = Long_Radian_L1,
         #                             lat1 = Lat_Radian_L1,
         #                             long2 = Long_Radian,
         #                             lat2 = Lat_Radian
         #                            ),
         
         TravelDistance_Mi_Hvrs = 
                              # ifelse((is.na(Longitude_L1) | is.na(Latitude_L1)
                              #        ),
                              #        NA,
                              distHaversine(cbind(Longitude_L1, Latitude_L1),
                                            cbind(Longitude, Latitude)
                                           ) * 0.000621371, # 0.000621371 miles = 1 meter
         
         # accounting for potential negative times
         TravelTime_Sec = as.numeric(ifelse(Event_Time > lag(Departure_Time),
                                            Event_Time - lag(Departure_Time),
                                            NA
                                           )
                                    ),
         TravelTime_Hr = TravelTime_Sec / 3600, # 3,600 seconds in 1 hour
         
         # accounting for potential negative or zero travel times
         SpeedAvg_Mph = ifelse(TravelTime_Hr > 0,
                               TravelDistance_Mi / TravelTime_Hr,
                               NA
                              ),
         
         Start_ID = lag(StopID_Clean),
         Start_Desc = lag(Stop_Desc),
         StartStop_ID = ifelse(is.na(Start_ID),
                               paste("NULL", StopID_Clean, sep = "--"),
                               paste(Start_ID, StopID_Clean, sep = "--")
                              )
        ) %>% 
  as.data.frame()


rm(AllDays_AddVars)
str(AllDays_BusDay)

# summary(AllDays_BusDay)

# View(tail(AllDays_BusDay, 50))

Inspecting for issues with StartStop_ID (where the value is either NA or contains NULL). They ONLY exist when BusDay_EventNum = 1 (which is by design). So everything looks OK.


View(group_by(AllDays_BusDay,
              StartStop_ID
             ) %>% 
       summarise(
         Cnt = n()
       ) %>% 
       arrange(desc(Cnt)
              )
    )

View(filter(AllDays_BusDay,
            (is.na(StartStop_ID) |
              str_detect(StartStop_ID, "NULL")
            ) &
              BusDay_EventNum != 1
           )
    )

Stats (quantiles) overall for TravelDistance_Mi.


Quantiles_dt <- AllDays_BusDay %>% 
  mutate(TD_Mi_q2 = quantile(x = TravelDistance_Mi, probs = 0.02, na.rm = TRUE),
         TD_Mi_q98 = quantile(x = TravelDistance_Mi, probs = 0.98, na.rm = TRUE),
         TT_Sec_q2 = quantile(x = TravelTime_Sec, probs = 0.02, na.rm = TRUE),
         TT_Sec_q98 = quantile(x = TravelTime_Sec, probs = 0.98, na.rm = TRUE),
         TT_Hr_q2 = quantile(x = TravelTime_Hr, probs = 0.02, na.rm = TRUE),
         TT_Hr_q98 = quantile(x = TravelTime_Hr, probs = 0.98, na.rm = TRUE)
        ) %>% 
  data.table()


Stats <- Quantiles_dt %>% 
  mutate(TD_Mi_Mean = mean(TravelDistance_Mi, na.rm = TRUE),
         TD_Mi_Mean_F = mean(TravelDistance_Mi[TD_Mi_q2 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_q98],
                             na.rm = TRUE
                            ),
         TD_Mi_Med = median(TravelDistance_Mi, na.rm = TRUE),
         TD_Mi_Med_F = median(TravelDistance_Mi[TD_Mi_q2 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_q98],
                              na.rm = TRUE
                             ),
         TD_Mi_Cnt = sum(!is.na(TravelDistance_Mi)
                        ),
         TD_Mi_Cnt_F = sum(!is.na(TravelDistance_Mi[TD_Mi_q2 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_q98]
                                 )
                          ),
            
         TT_Sec_Mean = mean(TravelTime_Sec, na.rm = TRUE),
         TT_Sec_Mean_F = mean(TravelTime_Sec[TT_Sec_q2 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_q98],
                              na.rm = TRUE
                             ),
         TT_Sec_Med = median(TravelTime_Sec, na.rm = TRUE),
         TT_Sec_Med_F = median(TravelTime_Sec[TT_Sec_q2 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_q98],
                               na.rm = TRUE
                              ),
         TT_Sec_Cnt = sum(!is.na(TravelTime_Sec)
                         ),
         TT_Sec_Cnt_F = sum(!is.na(TravelTime_Sec[TT_Sec_q2 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_q98]
                                   )
                           ),

         TT_Hr_Mean = mean(TravelTime_Hr, na.rm = TRUE),
         TT_Hr_Mean_F = mean(TravelTime_Hr[TT_Hr_q2 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_q98],
                             na.rm = TRUE
                            ),
         TT_Hr_Med = median(TravelTime_Hr, na.rm = TRUE),
         TT_Hr_Med_F = median(TravelTime_Hr[TT_Hr_q2 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_q98],
                              na.rm = TRUE
                             ),
         TT_Hr_Cnt = sum(!is.na(TravelTime_Hr)
                        ),
         TT_Hr_Cnt_F = sum(!is.na(TravelTime_Hr[TT_Hr_q2 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_q98]
                                 )
                          )
        ) %>% 
  data.frame()

rm(AllDays_BusDay)
rm(Quantiles_dt)
str(Stats)
# View(head(Stats, 50))

Stats for StartStop_ID.


Quantiles_SS_dt <- group_by(Stats,
                            StartStop_ID
                           ) %>% 
  mutate(TD_Mi_SS_q5 = quantile(x = TravelDistance_Mi, probs = 0.05, na.rm = TRUE),
         TD_Mi_SS_q95 = quantile(x = TravelDistance_Mi, probs = 0.95, na.rm = TRUE),
         TT_Sec_SS_q5 = quantile(x = TravelTime_Sec, probs = 0.05, na.rm = TRUE),
         TT_Sec_SS_q95 = quantile(x = TravelTime_Sec, probs = 0.95, na.rm = TRUE),
         TT_Hr_SS_q5 = quantile(x = TravelTime_Hr, probs = 0.05, na.rm = TRUE),
         TT_Hr_SS_q95 = quantile(x = TravelTime_Hr, probs = 0.95, na.rm = TRUE)
        ) %>% 
  data.table()


Stats_StSt <- group_by(Quantiles_SS_dt,
                       StartStop_ID
                      ) %>% 
  mutate(TD_Mi_SS_Mean = mean(TravelDistance_Mi, na.rm = TRUE),
         TD_Mi_SS_Mean_F = mean(TravelDistance_Mi[TD_Mi_SS_q5 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_SS_q95],
                                na.rm = TRUE
                               ),
         TD_Mi_SS_Med = median(TravelDistance_Mi, na.rm = TRUE),
         TD_Mi_SS_Med_F = median(TravelDistance_Mi[TD_Mi_SS_q5 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_SS_q95],
                                 na.rm = TRUE
                                ),
         TD_Mi_SS_Cnt = sum(!is.na(TravelDistance_Mi)
                           ),
         TD_Mi_SS_Cnt_F = sum(!is.na(TravelDistance_Mi[TD_Mi_SS_q5 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_SS_q95]
                                    )
                             ),
            
         TT_Sec_SS_Mean = mean(TravelTime_Sec, na.rm = TRUE),
         TT_Sec_SS_Mean_F = mean(TravelTime_Sec[TT_Sec_SS_q5 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_SS_q95],
                                 na.rm = TRUE
                                ),
         TT_Sec_SS_Med = median(TravelTime_Sec, na.rm = TRUE),
         TT_Sec_SS_Med_F = median(TravelTime_Sec[TT_Sec_SS_q5 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_SS_q95],
                                  na.rm = TRUE
                                 ),
         TT_Sec_SS_Cnt = sum(!is.na(TravelTime_Sec)),
         TT_Sec_SS_Cnt_F = sum(!is.na(TravelTime_Sec[TT_Sec_SS_q5 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_SS_q95]
                                     )
                              ),

         TT_Hr_SS_Mean = mean(TravelTime_Hr, na.rm = TRUE),
         TT_Hr_SS_Mean_F = mean(TravelTime_Hr[TT_Hr_SS_q5 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_SS_q95],
                                na.rm = TRUE
                               ),
         TT_Hr_SS_Med = median(TravelTime_Hr, na.rm = TRUE),
         TT_Hr_SS_Med_F = median(TravelTime_Hr[TT_Hr_SS_q5 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_SS_q95],
                                 na.rm = TRUE
                                ),
         TT_Hr_SS_Cnt = sum(!is.na(TravelTime_Hr)),
         TT_Hr_SS_Cnt_F = sum(!is.na(TravelTime_Hr[TT_Hr_SS_q5 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_SS_q95]
                                    )
                             )
        ) %>% 
  data.frame()

rm(Stats)
rm(Quantiles_SS_dt)
str(Stats_StSt)
# View(head(Stats_StSt, 50))

Stats for StartStop_ID with Event_Time_HrGroup.


Quantiles_SSHG_dt <- group_by(Stats_StSt,
                              StartStop_ID,
                              Event_Time_HrGroup
                             ) %>% 
  mutate(TD_Mi_SSHG_q5 = quantile(x = TravelDistance_Mi, probs = 0.05, na.rm = TRUE),
         TD_Mi_SSHG_q95 = quantile(x = TravelDistance_Mi, probs = 0.95, na.rm = TRUE),
         TT_Sec_SSHG_q5 = quantile(x = TravelTime_Sec, probs = 0.05, na.rm = TRUE),
         TT_Sec_SSHG_q95 = quantile(x = TravelTime_Sec, probs = 0.95, na.rm = TRUE),
         TT_Hr_SSHG_q5 = quantile(x = TravelTime_Hr, probs = 0.05, na.rm = TRUE),
         TT_Hr_SSHG_q95 = quantile(x = TravelTime_Hr, probs = 0.95, na.rm = TRUE)
        ) %>% 
  data.table()


Stats_StSt_HrGrp <- group_by(Quantiles_SSHG_dt,
                             StartStop_ID,
                             Event_Time_HrGroup
                            ) %>% 
  mutate(TD_Mi_SSHG_Mean = mean(TravelDistance_Mi, na.rm = TRUE),
         TD_Mi_SSHG_Mean_F = mean(TravelDistance_Mi[TD_Mi_SSHG_q5 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_SSHG_q95],
                                  na.rm = TRUE
                                 ),
         TD_Mi_SSHG_Med = median(TravelDistance_Mi, na.rm = TRUE),
         TD_Mi_SSHG_Med_F = median(TravelDistance_Mi[TD_Mi_SSHG_q5 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_SSHG_q95],
                                   na.rm = TRUE
                                  ),
         TD_Mi_SSHG_Cnt = sum(!is.na(TravelDistance_Mi)
                             ),
         TD_Mi_SSHG_Cnt_F = sum(!is.na(TravelDistance_Mi[TD_Mi_SSHG_q5 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_SSHG_q95]
                                      )
                               ),
            
         TT_Sec_SSHG_Mean = mean(TravelTime_Sec, na.rm = TRUE),
         TT_Sec_SSHG_Mean_F = mean(TravelTime_Sec[TT_Sec_SSHG_q5 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_SSHG_q95],
                                   na.rm = TRUE
                                  ),
         TT_Sec_SSHG_Med = median(TravelTime_Sec, na.rm = TRUE),
         TT_Sec_SSHG_Med_F = median(TravelTime_Sec[TT_Sec_SSHG_q5 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_SSHG_q95],
                                    na.rm = TRUE
                                   ),
         TT_Sec_SSHG_Cnt = sum(!is.na(TravelTime_Sec)),
         TT_Sec_SSHG_Cnt_F = sum(!is.na(TravelTime_Sec[TT_Sec_SSHG_q5 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_SSHG_q95]
                                       )
                                ),

         TT_Hr_SSHG_Mean = mean(TravelTime_Hr, na.rm = TRUE),
         TT_Hr_SSHG_Mean_F = mean(TravelTime_Hr[TT_Hr_SSHG_q5 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_SSHG_q95],
                                  na.rm = TRUE
                                 ),
         TT_Hr_SSHG_Med = median(TravelTime_Hr, na.rm = TRUE),
         TT_Hr_SSHG_Med_F = median(TravelTime_Hr[TT_Hr_SSHG_q5 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_SSHG_q95],
                                   na.rm = TRUE
                                  ),
         TT_Hr_SSHG_Cnt = sum(!is.na(TravelTime_Hr)),
         TT_Hr_SSHG_Cnt_F = sum(!is.na(TravelTime_Hr[TT_Hr_SSHG_q5 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_SSHG_q95]
                                      )
                               )
        ) %>% 
  data.frame()

rm(Stats_StSt)
rm(Quantiles_SSHG_dt)
str(Stats_StSt_HrGrp)
# View(head(Stats_StSt_HrGrp, 50))

Feature engineering.

Creating a BusEventRoute row number, and a RouteAlt_Lag1 indicator for future identification purposes.


# rm(Quantiles_dt)
# rm(Quantiles_SS_dt)
# rm(AllDays_BusDay)
# rm(Quantiles_SSHG_dt)
# rm(Stats_StSt)

# AllDays_BusDayRoute <- group_by(Stats_StSt_HrGrp,
#                                 Bus_ID,
#                                 Event_Time_Date,
#                                 Route
#                                ) %>% 
#   mutate(RouteAlt_Lag2 = lag(RouteAlt)  # used in future analyses to identify RouteAlt (direction) changes
#          
#          # Odometer_Distance_Lag1 = lag(Odometer_Distance),
#          # 
#          # # accounting for potential negative distances
#          # TravelDistance_Ft = ifelse(Odometer_Distance >= Odometer_Distance_Lag1,
#          #                            Odometer_Distance - Odometer_Distance_Lag1,
#          #                            NA
#          #                           ),
#          # TravelDistance_Mi = TravelDistance_Ft / 5280, #5,280 feet in 1 mile
#          # 
#          # # accounting for potential negative times
#          # TravelTime_Sec = as.numeric(ifelse(Event_Time >= lag(Departure_Time),
#          #                                    Event_Time - lag(Departure_Time),
#          #                                    NA
#          #                                   )
#          #                            ),
#          # TravelTime_Hr = TravelTime_Sec / 3600, # 3,600 seconds in 1 hour
#          # 
#          # # accounting for potential negative or zero travel times
#          # SpeedAvg_Mph = ifelse(TravelTime_Hr > 0,
#          #                       TravelDistance_Mi / TravelTime_Hr,
#          #                       NA
#          #                      )
#         ) %>% 
#   data.frame()
# 
# rm(Stats_StSt_HrGrp)
# str(AllDays_BusDayRoute)

Feature engineering.

Calculating a variable to know if the RouteAlt changed. Could be useful in helping identifying weirdness in calculated distances and speeds.


# rm(Stats_StSt_HrGrp)

AllDays_DirChange <- Stats_StSt_HrGrp %>%  # AllDays_BusDayRoute %>% 
  mutate(RteChange = ifelse(Route == Route_Lag1,
                            "Same",
                            "Change"
                           ),
         RteChange2 = factor(ifelse(is.na(RteChange),
                                    "Change",
                                    RteChange
                                   )
                            ),
         DirChange = ifelse(RouteAlt == RouteAlt_Lag1,
                            "Same",
                            "Change"
                           ),
         DirChange2 = factor(ifelse(is.na(DirChange),
                                    "Change",
                                    DirChange
                                   )
                            )
        )

# rm(AllDays_BusDayRoute)
rm(Stats_StSt_HrGrp)
str(AllDays_DirChange)

View(filter(AllDays_DirChange,
            between(RowNum_OG, 2570060, 2570080)
           ) %>% 
       select(-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
             )
    )

Re-ordering the variables to ease with comprehension.


AllDays_NewOrder <-  select(AllDays_DirChange,
                            RowNum_OG,
                            UniqueLatLng,
                            group,
                            StartStop_ID,
                            BusDay_EventNum,
                            Bus_ID,
                            Route,
                            RteChange2,
                            RouteAlt,
                            # RouteAlt_Lag1,
                            DirChange2,
                            Route_Direction,
                            Stop_Sequence,
                            Start_ID,
                            Start_Desc,
                            # Stop_ID,
                            StopID_Clean,
                            StopID_Indicator,
                            Stop_Desc,
                            countryCode,
                            Stop_State,
                            Stop_County,
                            Stop_City,
                            Stop_Zip,
                            Event_Type,
                            Event_Description,
                            Event_Time_Yr,
                            Event_Time_Mth,
                            Event_Time_Date,
                            Event_Time_Day,
                            Event_Time_Hr,
                            Event_Time_HrGroup,
                            Event_Time_Min,
                            Event_Time,
                            Departure_Time,
                            Dwell_Time,
                            Dwell_Time2,
                            Delta_Time,
                            Latitude,
                            Longitude,
                            Heading,
                            Odometer_Distance,
                            Odometer_Distance_Lag1,
                            Odometer_Distance_Mi,
                            TravelDistance_Ft,
                            TravelDistance_Mi,
                            TravelDistance_Mi_Hvrs,
                            TD_Mi_q2,
                            TD_Mi_q98,
                            TD_Mi_SS_q5,
                            TD_Mi_SS_q95,
                            TD_Mi_SSHG_q5,
                            TD_Mi_SSHG_q95,
                            TD_Mi_Mean,
                            TD_Mi_Mean_F,
                            TD_Mi_SS_Mean,
                            TD_Mi_SS_Mean_F,
                            TD_Mi_SSHG_Mean,
                            TD_Mi_SSHG_Mean_F,
                            TD_Mi_Med,
                            TD_Mi_Med_F,
                            TD_Mi_SS_Med,
                            TD_Mi_SS_Med_F,
                            TD_Mi_SSHG_Med,
                            TD_Mi_SSHG_Med_F,
                            TD_Mi_Cnt,
                            TD_Mi_Cnt_F,
                            TD_Mi_SS_Cnt,
                            TD_Mi_SS_Cnt_F,
                            TD_Mi_SSHG_Cnt,
                            TD_Mi_SSHG_Cnt_F,
                            TravelTime_Sec,
                            TT_Sec_q2,
                            TT_Sec_q98,
                            TT_Sec_SS_q5,
                            TT_Sec_SS_q95,
                            TT_Sec_SSHG_q5,
                            TT_Sec_SSHG_q95,
                            TT_Sec_Mean,
                            TT_Sec_Mean_F,
                            TT_Sec_SS_Mean,
                            TT_Sec_SS_Mean_F,
                            TT_Sec_SSHG_Mean,
                            TT_Sec_SSHG_Mean_F,
                            TT_Sec_Med,
                            TT_Sec_Med_F,
                            TT_Sec_SS_Med,
                            TT_Sec_SS_Med_F,
                            TT_Sec_SSHG_Med,
                            TT_Sec_SSHG_Med_F,
                            TT_Sec_Cnt,
                            TT_Sec_Cnt_F,
                            TT_Sec_SS_Cnt,
                            TT_Sec_SS_Cnt_F,
                            TT_Sec_SSHG_Cnt,
                            TT_Sec_SSHG_Cnt_F,
                            TravelTime_Hr,
                            TT_Hr_q2,
                            TT_Hr_q98,
                            TT_Hr_SS_q5,
                            TT_Hr_SS_q95,
                            TT_Hr_SSHG_q5,
                            TT_Hr_SSHG_q95,
                            TT_Hr_Mean,
                            TT_Hr_Mean_F,
                            TT_Hr_SS_Mean,
                            TT_Hr_SS_Mean_F,
                            TT_Hr_SSHG_Mean,
                            TT_Hr_SSHG_Mean_F,
                            TT_Hr_Med,
                            TT_Hr_Med_F,
                            TT_Hr_SS_Med,
                            TT_Hr_SS_Med_F,
                            TT_Hr_SSHG_Med,
                            TT_Hr_SSHG_Med_F,
                            TT_Hr_Cnt,
                            TT_Hr_Cnt_F,
                            TT_Hr_SS_Cnt,
                            TT_Hr_SS_Cnt_F,
                            TT_Hr_SSHG_Cnt,
                            TT_Hr_SSHG_Cnt_F,
                            SpeedAvg_Mph
                           )

rm(AllDays_DirChange)
str(select(AllDays_NewOrder,
           -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
          )
   )
str(AllDays_NewOrder)

# View(head(AllDays_NewOrder, 500))
# View(tail(AllDays_NewOrder, 500))

Summarizing the data to help spot anomolies.


View(group_by(AllDays_NewOrder,
              Stop_City) %>% 
       summarise(Cnt_Num = n(),
                 Cnt_Pct = 100*Cnt_Num / (nrow(AllDays_NewOrder)
                                         )
                ) %>% 
       arrange(desc(Cnt_Num))
)

summary(AllDays_NewOrder)

Investigation of TravelDistance_Mi.

View(TravDistMi_Pctiles): 99% of TravelDistance_Mi are about 1 mile or less…but some weird TravelDistance_Mi values (e.g., 584 miles traveled) exist.


TravDistMi_Ntile <- as.data.frame(AllDays_NewOrder$TravelDistance_Mi) %>% 
  mutate(#Pctile = ntile(AllDays_NewOrder$TravelDistance_Mi, 100),
         #MinR = min_rank(AllDays_NewOrder$TravelDistance_Mi),
         PctR = percent_rank(AllDays_NewOrder$TravelDistance_Mi),
         PctR_Round = round(PctR, 2)
        ) 

colnames(TravDistMi_Ntile)[1] <- "TravelDistance_Mi"
# str(TravDistMi_Ntile)

TravDistMi_Ntile_Rows <- nrow(TravDistMi_Ntile)

# View(tail(TravDistMi_Ntile, 500))


TravDistMi_Pctiles <- group_by(TravDistMi_Ntile,
                               PctR_Round
                              ) %>% 
  summarise(
    MinTravDistMiAtPctile = min(TravelDistance_Mi),
    CntsAtPctile = n(),
    PctsAtPctile = CntsAtPctile / TravDistMi_Ntile_Rows
  ) %>% 
  mutate(CumSumPAtP = cumsum(PctsAtPctile)
        )

rm(TravDistMi_Ntile)
rm(TravDistMi_Ntile_Rows)

View(TravDistMi_Pctiles)
TravDistMi_Pctiles

Investigation of TravelDistance_Mi.

Why are some TravelDistance_Mi “NA”? It looks like partially because the records are the first trip of the day (for that bus), so I purposefully set the distance to “NA”. Another reason is due to the odometer recording a value less than the previous odometer recording. In most cases, I have no explanation for this - though I have observed about 67% of all instances where TravelDistance_Mi is NA (other than because it’s the first record of the day) are instances where DirChange2 is “Change”. This is weird and should be asked to WMATA.


# View(head(AllDays_NewOrder, 500))

View(filter(AllDays_NewOrder,
            BusDay_EventNum != 1 # When BusDay_EventNum == 1, TravelDistance_Mi is NA by design (don't want to calculate distance based on yesterday's position)
           ) %>% 
       group_by(StartStop_ID) %>% 
       summarise(Cnts = sum(is.na(TravelDistance_Mi)
                           )
                ) %>% 
       arrange(desc(Cnts)
              )
    )

View(filter(AllDays_NewOrder,
            StartStop_ID == "1000245--1000211"
           ) %>% 
       select(RowNum_OG,
              StartStop_ID,
              Event_Time,
              Event_Time_HrGroup,
              Bus_ID,
              TravelDistance_Mi,
              TravelDistance_Mi_Hvrs,
              TD_Mi_SS_Mean,
              TD_Mi_SS_Mean_F,
              TD_Mi_SSHG_Mean,
              TD_Mi_SSHG_Mean_F,
              TD_Mi_SS_Med,
              TD_Mi_SS_Med_F,
              TD_Mi_SSHG_Med,
              TD_Mi_SSHG_Med_F,
              TD_Mi_SS_Cnt,
              TD_Mi_SS_Cnt_F,
              TD_Mi_SSHG_Cnt,
              TD_Mi_SSHG_Cnt_F
              ) %>% 
       mutate(Ratio_MeanToHvrs = TD_Mi_SS_Mean / TravelDistance_Mi_Hvrs) %>% 
       arrange(Event_Time)
    )

View(filter(AllDays_NewOrder,
            is.na(TravelDistance_Mi)
           )
    )

# These records are NA becuase the record is the first record of the day (the Event_Time_Date)
View(filter(AllDays_NewOrder,
            between(RowNum_OG, 326, 346) | # 336
              between(RowNum_OG, 591, 611) | # 601
              between(RowNum_OG, 845, 865) # 855
           )
    )

Investigation of TravelDistance_Mi.

These records are NA becuase the current record odometer is less than the previous record odometer. Theoretically, this should NOT happen. Me: it appears that about 67% of all instances where TravelDistance_Mi is NA (other than because it’s th first record of the day) are instances where DirChange2 is “Change”. This is weird and should be asked to WMATA.


View(filter(AllDays_NewOrder,
            between(RowNum_OG, 194, 214) | # 204
              between(RowNum_OG, 440, 460) | # 450
              between(RowNum_OG, 478, 498) | # 488
              between(RowNum_OG, 510, 530) # 520
           )
    )

TestTable <- filter(AllDays_NewOrder,
                    BusDay_EventNum != 1
                   ) %>% 
  mutate(TravelDistance_NA = as.factor(ifelse(is.na(TravelDistance_Mi),
                                              "True",
                                              "False"
                                             )
                                      )
        ) %>%
  group_by(DirChange2, TravelDistance_NA) %>%
  summarise(TravDistMi_NACnts = n()
           )

# TestTable

TestTable_Spread <- as.data.frame(spread(TestTable,
                                         TravelDistance_NA,
                                         TravDistMi_NACnts
                                        )
                                 ) %>% 
  select(False,
         True
        )

row.names(TestTable_Spread) <- c("Change", "Same")
# str(TestTable_Spread)
# TestTable_Spread

prop.table(as.table(as.matrix(TestTable_Spread)
                   ),
           1
          )

prop.table(as.table(as.matrix(TestTable_Spread)
                   ),
           2
          )

Investigation of TravelDistance_Mi.

Let’s look at just the TravelDistance_Mi values that are NOT “NA”.


rm(TestTable, TestTable_Spread)

TravelDistance_Mi_NoNA <- filter(AllDays_NewOrder,
                                 # TravelDistance_Mi != 0 &
                                 !is.na(TravelDistance_Mi)
                                )

dim(AllDays_NewOrder)
dim(TravelDistance_Mi_NoNA)
nrow(AllDays_NewOrder) - nrow(TravelDistance_Mi_NoNA)

str(TravelDistance_Mi_NoNA)
summary(TravelDistance_Mi_NoNA)

Investigation of TravelDistance_Mi.

Let’s plot just the TravelDistance_Mi values that are NOT “NA”.


TravDistMi_HistDen <- ggplot(select(TravelDistance_Mi_NoNA,
                                    TravelDistance_Mi
                                   ),
                             aes(x = TravelDistance_Mi,
                                 y = ..density..
                                )
                            ) +
  geom_histogram(binwidth = 0.05, fill = "lightblue", colour = "grey60", size = 0.2) +
  geom_line(stat = "density", colour = "red") +
  coord_cartesian(xlim = c(0, 1.5), ylim = c(0, 4.0)
                 ) +
  labs(title = "Variation in Distance Between Stops",
       x = "Travel Distance (miles)",
       y = "Density"
      )

TravDistMi_HistDen

Investigation of TravelDistance_Mi.

Looking at the extremely large TravelDistance_Mi values. Some (aprox 27%) of TravelDistance_Mi values > 1 mile are when the DirChange2 changes…but what about the other ~73%?


rm(TravelDistance_Mi_NoNA)

# examples of weirdly large TravelDistance_Mi
View(filter(AllDays_NewOrder,
            TravelDistance_Mi > 1.1587121212 # 1.1587121212 is the 99th percentile
           ) %>% 
       arrange(desc(TravelDistance_Mi)
              )
    )


# Why are these extremes?  Airports?  Bus collection points?
View(filter(AllDays_NewOrder,
              between(RowNum_OG, 494044, 494064) | # 494054
              between(RowNum_OG, 494273, 494293) | # 494283
              between(RowNum_OG, 494626, 494646) | # 494636
              between(RowNum_OG, 1610156, 1610176) | # 1610166
              between(RowNum_OG, 2073074, 2073094) # 2073084
           )
    )

# Before Removing Runs
# View(filter(AllDays_Sorted,
#             between(RowNum_OG, 494044, 494064) | # 494054
#               between(RowNum_OG, 494273, 494293) | # 494283
#               between(RowNum_OG, 494626, 494646) | # 494636
#               between(RowNum_OG, 1610156, 1610176) | # 1610166
#               between(RowNum_OG, 2073074, 2073094) # 2073084
#            )
#     )

# After Removing Runs
# View(filter(AllDays_FirstStopID,
#             between(RowNum_OG, 494044, 494064) | # 494054
#               between(RowNum_OG, 494273, 494293) | # 494283
#               between(RowNum_OG, 494626, 494646) | # 494636
#               between(RowNum_OG, 1610156, 1610176) | # 1610166
#               between(RowNum_OG, 2073074, 2073094) # 2073084
#            )
#     )

Investigation of TravelDistance_Mi.

Any relation with DirChange2? Doesn’t look as if this is so.


ExtremeTravDist <- filter(AllDays_NewOrder,
                          !is.na(TravelDistance_Mi)
                         ) %>% 
  mutate(TravDist_Extreme = ifelse(TravelDistance_Mi > 1.1587121212, # 1.1587121212 is the 99th percentile
                                   "True",
                                   "False"
                                  )
                          ) %>% 
  group_by(DirChange2, TravDist_Extreme) %>% 
  summarise(TravDistMI_ExtCnts = n()
           )

# ExtremeTravDist


ExtremeTravDist_Spread <- as.data.frame(spread(ExtremeTravDist,
                                               TravDist_Extreme,
                                               TravDistMI_ExtCnts
                                              )
                                       ) %>% 
  select(False,
         True
        )

row.names(ExtremeTravDist_Spread) <- c("Change", "Same")
# str(ExtremeTravDist_Spread)
# ExtremeTravDist_Spread

prop.table(as.table(as.matrix(ExtremeTravDist_Spread)
                   ),
           1
          )

prop.table(as.table(as.matrix(ExtremeTravDist_Spread)
                   ),
           2
          )

Investigation of TravelDistance_Mi.

Looking at specific buses and StartStop_ID.


rm(ExtremeTravDist, ExtremeTravDist_Spread)

View(arrange(group_by(AllDays_NewOrder,
                      Bus_ID
                     ) %>% 
               summarise(DistTrav_Mean = mean(TravelDistance_Mi, na.rm = TRUE),
                         DistTrav_Med = median(TravelDistance_Mi, na.rm = TRUE)
                        ),
             desc(DistTrav_Med)
            )
    )


# example of extremely small TravelDistance_Mi values (looks like the odometer wasn't functioning)
View(filter(AllDays_NewOrder,
            Bus_ID == 6111 |
              Bus_ID == 7201 |
              Bus_ID == 8058
           ) %>% 
       arrange(Bus_ID, Event_Time)
    )


View(arrange(group_by(AllDays_NewOrder,
                      StartStop_ID
                     ) %>% 
               summarise(DistTrav_Mean = mean(TravelDistance_Mi, na.rm = TRUE),
                         DistTrav_Med = median(TravelDistance_Mi, na.rm = TRUE)
                        ),
             desc(DistTrav_Med)
            )
    )

# example of extremely large TravelDistance_Mi values...no idea why...
View(filter(AllDays_NewOrder,
            StartStop_ID == "1003665--12" |
              StartStop_ID == "1003665--5001925" |
              StartStop_ID == "3001038--3002565"
           ) %>% 
       arrange(StartStop_ID, Event_Time)
    )

Investigation of TravelDistance_Mi & TravelDistance_Mi_New.

If TravelDisntace_Mi is below the 5th percentile for that StartStop_ID, or if TravelDisntace_Mi is above the 95th percentile for that StartStop_ID, or if TravelDistance_Mi is NA (when the BusDay_EventNum !=1), consider this an outlier. In this case, replace the value with the mean for that StartStop_ID and HourGroup (TD_Mi_SSHG_Mean_F), or if there are not enough values at the HourGroup level, replace it with the mean for that StartStop_ID.


# View(tail(AllDays_NewOrder, 500))

AllDays_NewTravelDist <- 
  mutate(AllDays_NewOrder,
         TravelDistance_Mi_New = ifelse(!is.na(TravelDistance_Mi) & 
                                          (TravelDistance_Mi < TD_Mi_SSHG_q5 |
                                             TravelDistance_Mi > TD_Mi_SSHG_q95
                                          ) &
                                          TD_Mi_SSHG_Cnt_F >= 20,
                                        TD_Mi_SSHG_Mean_F,
                                 ifelse(!is.na(TravelDistance_Mi) & 
                                          (TravelDistance_Mi < TD_Mi_SSHG_q5 |
                                             TravelDistance_Mi > TD_Mi_SSHG_q95
                                          ) &
                                          TD_Mi_SSHG_Cnt_F < 20 &
                                          TD_Mi_SS_Cnt_F >= 20,
                                        TD_Mi_SS_Mean_F,
                                 ifelse(!is.na(TravelDistance_Mi) & 
                                          (TravelDistance_Mi < TD_Mi_SSHG_q5 |
                                             TravelDistance_Mi > TD_Mi_SSHG_q95
                                          ) &
                                          TD_Mi_SS_Cnt_F < 20 &
                                          TD_Mi_SS_Cnt >= 20,
                                        TD_Mi_SS_Mean,
                                 ifelse(is.na(TravelDistance_Mi) &
                                          BusDay_EventNum != 1 &
                                          TravelDistance_Mi_Hvrs != 0,
                                        TravelDistance_Mi_Hvrs,
                                 ifelse(is.na(TravelDistance_Mi) &
                                          BusDay_EventNum != 1 &
                                          TravelDistance_Mi_Hvrs == 0,
                                        TD_Mi_SS_Mean,
                                        TravelDistance_Mi
                                       ))))),
         TravelDistance_Mi_New_Label = 
           factor(ifelse(!is.na(TravelDistance_Mi) &
                           (TravelDistance_Mi < TD_Mi_SSHG_q5 |
                              TravelDistance_Mi > TD_Mi_SSHG_q95
                           ) &
                           TD_Mi_SSHG_Cnt_F >= 20,
                         "TD_Mi_SSHG_Mean_F",
                  ifelse(!is.na(TravelDistance_Mi) &
                           (TravelDistance_Mi < TD_Mi_SSHG_q5 |
                              TravelDistance_Mi > TD_Mi_SSHG_q95
                           ) &
                           TD_Mi_SSHG_Cnt_F < 20 &
                           TD_Mi_SS_Cnt_F >= 20,
                         "TD_Mi_SS_Mean_F",
                  ifelse(!is.na(TravelDistance_Mi) &
                           (TravelDistance_Mi < TD_Mi_SSHG_q5 |
                              TravelDistance_Mi > TD_Mi_SSHG_q95
                           ) &
                           TD_Mi_SS_Cnt_F < 20 &
                           TD_Mi_SS_Cnt >= 20,
                         "TD_Mi_SS_Mean",
                  ifelse(is.na(TravelDistance_Mi) &
                           BusDay_EventNum != 1 &
                           TravelDistance_Mi_Hvrs != 0,
                         "TravelDistance_Mi_Hvrs",
                  ifelse(is.na(TravelDistance_Mi) &
                           BusDay_EventNum != 1 &
                           TravelDistance_Mi_Hvrs == 0,
                         "TD_Mi_SS_Mean",
                         "TravelDistance_Mi"
                        )))))
                 ),
         TravelDistance_Mi_NewHvrs = ifelse(!is.na(TravelDistance_Mi_Hvrs) &
                                              TravelDistance_Mi_Hvrs != 0 &
                                              (TravelDistance_Mi_New < TD_Mi_q2 |
                                                 TravelDistance_Mi_New > TD_Mi_q98
                                              ),
                                            TravelDistance_Mi_Hvrs,
                                            TravelDistance_Mi_New
                                           ),
         TravelDistance_Mi_NewHvrs_Label =
           factor(ifelse(!is.na(TravelDistance_Mi_Hvrs) &
                           TravelDistance_Mi_Hvrs != 0 &
                           (TravelDistance_Mi_New < TD_Mi_q2 |
                              TravelDistance_Mi_New > TD_Mi_q98
                           ),
                         "TravelDistance_Mi_Hvrs",
                         as.character(TravelDistance_Mi_New_Label)
                        )
                 ),
         SpeedAvg_Mph_NewHvrs = TravelDistance_Mi_NewHvrs / TravelTime_Hr
        )

rm(AllDays_NewOrder)
str(AllDays_NewTravelDist)

Investigation of TravelDistance_Mi & TravelDistance_Mi_Hvrs & TravelDistance_Mi_New.

Quick summary and then correlation calculation.


# 38 rows meet this criteria anymore  --  appears to be the case when both the Lat Long calculations, and the TravelDistance calculations did not function properly.
View(filter(AllDays_NewTravelDist,
            is.na(TravelDistance_Mi_New) &
              BusDay_EventNum != 1
           )
    )

View(AllDays_NewTravelDist %>% 
       arrange(desc(TravelDistance_Mi_New)) %>% 
       head(500)
    )

summary(select(AllDays_NewTravelDist,
               TravelDistance_Mi,
               TravelDistance_Mi_Hvrs,
               TravelDistance_Mi_New,
               TravelDistance_Mi_NewHvrs
              )
       )

summary(select(filter(AllDays_NewTravelDist,
                      BusDay_EventNum != 1
                     ),
               TravelDistance_Mi,
               TravelDistance_Mi_Hvrs,
               TravelDistance_Mi_New,
               TravelDistance_Mi_NewHvrs
              )
       )


cor(select(AllDays_NewTravelDist,
           TravelDistance_Mi,
           TravelDistance_Mi_Hvrs,
           TravelDistance_Mi_New,
           TravelDistance_Mi_NewHvrs
          ),
    use = "pairwise.complete.obs"
  )

Investigation of TravelDistance_Mi_NewHvrs_Label & TravelDistance_Mi_NewHvrs_Label.

Show how the labels changed.


group_by(AllDays_NewTravelDist,
         TravelDistance_Mi_New_Label,
         TravelDistance_Mi_NewHvrs_Label
        ) %>% 
  summarise(CntNum = n(),
            CntPct = format(CntNum / nrow(AllDays_NewTravelDist),
                            scientific = 9999
                           )
           ) %>% 
  arrange(desc(CntPct)
         )

Investigation of TravelDistance_Mi & TravelDistance_Mi_Hvrs & TravelDistance_Mi_New.

Graphing the two methods of calculating TravelDistance_Mi.

First, let’s get create a function to plot the liner model equation.


lm_eqn <- function(df, y, x){
  m <- lm(y ~ x, df)
  
  l <- list(a = format(coef(m)[1], digits = 2),
            b = format(abs(coef(m)[2]), digits = 2),
            s1 = ifelse(test = coef(m)[2] > 0,
                        yes = "+",
                        no = "-"
                       ),
            r2 = format(summary(m)$r.squared,
                        digits = 3
                       )
           )
  
  eq <- substitute(italic(y) == a~~s1~~b %.% italic(x)*","~~italic(r)^2~"="~r2,
                   l
                  )
  
  as.character(as.expression(eq)
              )             
}

Investigation of TravelDistance_Mi & TravelDistance_Mi_NewHvrs.

Scatter plot (using a 10% sample to making plotting time faster and to reduce un-needed data in the “same” splot).


set.seed(123456789)
AllDays_NewTravelDist_10Pct <- filter(AllDays_NewTravelDist,
                                      !is.na(TravelDistance_Mi_NewHvrs) &
                                        !is.na(TravelDistance_Mi)
                                     ) %>% 
  rename(DistMethod = TravelDistance_Mi_NewHvrs_Label) %>% 
  sample_frac(0.1)


TravDist_MiVsCalc <- ggplot(select(AllDays_NewTravelDist_10Pct,
                                   TravelDistance_Mi_NewHvrs,
                                   TravelDistance_Mi,
                                   DistMethod
                                  ),
                            aes(x = TravelDistance_Mi,
                                y = TravelDistance_Mi_NewHvrs,
                                colour = DistMethod
                               )
                           ) +
  scale_colour_manual(values = c("red","blue", "green", "orange", "black")
                     ) +
  geom_point(shape = 1, alpha = 0.5) +
  scale_shape(solid = FALSE) +
  geom_smooth(method = "lm", colour = "blue") +
  geom_abline(intercept = 0, slope = 1, colour = "red") +
  coord_cartesian(xlim = c(0, 1.5), ylim = c(0, 1.5)
                 ) +
  scale_x_continuous(breaks = seq(0, 1.5, 0.25)
                    ) +
  scale_y_continuous(breaks = seq(0, 1.5, 0.25)
                    ) +
  theme(legend.position = "bottom", #c(0.85, 0.40),
        legend.text = element_text(size = 6)
       ) +
  annotate(label = lm_eqn(df = AllDays_NewTravelDist_10Pct,
                          x = AllDays_NewTravelDist_10Pct$TravelDistance_Mi,
                          y = AllDays_NewTravelDist_10Pct$TravelDistance_Mi_NewHvrs
                         ),
           # x = 62,
           # y = 20,
           x = 0.70,
           y = 0.00,
           geom = "text",
           size = 3,
           colour = "blue",
           parse = TRUE
          ) +
  annotate(label = "Reference Line (slope = 1)",
           # x = 16,
           # y = 30,
           x = 0.80,
           y = 1.05,
           geom = "text",
           size = 3,
           colour = "red"
          ) +
  labs(title = "TravelDistance_Mi vs. TravelDistance_Mi_NewHvrs",
       x = "TravelDistance_Mi",
       y = "TravelDistance_Mi_NewHvrs"
      )
# +
#   geom_jitter()

TravDist_MiVsCalc

Investigation of TravelDistance_Mi & TravelDistance_Mi_Hvrs & TravelDistance_Mi_New.

Graphing test with rbokeh.


TravDist_MiVsCalc_Bokeh <- figure(data = select(AllDays_NewTravelDist_10Pct,
                                                TravelDistance_Mi_NewHvrs,
                                                TravelDistance_Mi,
                                                DistMethod
                                               ),
                                  xlim = c(0, 1.5),
                                  ylim = c(0, 1.5),
                                  legend_location = "bottom_right"
                                 ) %>% 
  ly_points(x = TravelDistance_Mi,
            y = TravelDistance_Mi_NewHvrs,
            color = DistMethod,
            hover = c(TravelDistance_Mi_NewHvrs, TravelDistance_Mi, DistMethod)
           ) %>% 
  ly_abline(a = 0, b = 1, color = "red")

TravDist_MiVsCalc_Bokeh

Investigation of TravelDistance_Mi_New.

Calculating the minimum TravelDistance_Mi_New value at each percentile.


rm(TravDist_MiVsCalc_Bokeh)
rm(AllDays_NewTravelDist_10Pct)


summary(select(AllDays_NewTravelDist,
               TravelDistance_Mi,
               TravelDistance_Mi_Hvrs,
               TravelDistance_Mi_New,
               TravelDistance_Mi_NewHvrs
              )
       )

summary(select(filter(AllDays_NewTravelDist,
                      BusDay_EventNum != 1
                     ),
               TravelDistance_Mi,
               TravelDistance_Mi_Hvrs,
               TravelDistance_Mi_New,
               TravelDistance_Mi_NewHvrs
              )
       )


TravDistMiN_Ntile <- as.data.frame(select(AllDays_NewTravelDist,
                                          StartStop_ID,
                                          TravelDistance_Mi_New_Label,
                                          # TravelDistance_Mi_NewHvrs_Label,
                                          TravelDistance_Mi_New
                                          # TravelDistance_Mi_NewHvrs
                                         )
                                  ) %>% 
  mutate(PctR_N = percent_rank(AllDays_NewTravelDist$TravelDistance_Mi_New),
         # PctR_H = percent_rank(AllDays_NewTravelDist$TravelDistance_Mi_NewHvrs),
         PctR_Round_N = round(PctR_N, 2)
         # PctR_Round_H = round(PctR_H, 2)
        ) 

# str(TravDistMiN_Ntile)
# View(head(TravDistMiN_Ntile, 500))

TravDistMiN_Ntile_Rows <- nrow(TravDistMiN_Ntile)

# View(tail(TravDistMiN_Ntile, 500))


TravDistMiN_Pctiles <- group_by(TravDistMiN_Ntile,
                                PctR_Round_N
                               ) %>% 
  summarise(
    MinTDMiAtPctile_N = min(TravelDistance_Mi_New),
    # MinTDMiAtPctile_H = min(TravelDistance_Mi_NewHvrs),
    CntsAtPctile_N = sum(!is.na(TravelDistance_Mi_New)),
    # CntsAtPctile_H = sum(!is.na(TravelDistance_Mi_NewHvrs)),
    PctsAtPctile_N = CntsAtPctile_N / TravDistMiN_Ntile_Rows
    # PctsAtPctile_H = CntsAtPctile_H / TravDistMiN_Ntile_Rows
  ) %>% 
  mutate(CumSumPAtP_N = cumsum(PctsAtPctile_N)
         # CumSumPAtP_H = cumsum(PctsAtPctile_H)
        )

# View(TravDistMiN_Pctiles)
TravDistMiN_Pctiles

Investigation of TravelDistance_Mi_NewHvrs

Calculating the minimum TravelDistance_Mi_NewHvrs value at each percentile.


TravDistMiH_Ntile <- as.data.frame(select(AllDays_NewTravelDist,
                                          StartStop_ID,
                                          # TravelDistance_Mi_New_Label,
                                          TravelDistance_Mi_NewHvrs_Label,
                                          # TravelDistance_Mi_New,
                                          TravelDistance_Mi_NewHvrs
                                         )
                                  ) %>% 
  mutate(# PctR_N = percent_rank(AllDays_NewTravelDist$TravelDistance_Mi_New),
         PctR_H = percent_rank(AllDays_NewTravelDist$TravelDistance_Mi_NewHvrs),
         # PctR_Round_N = round(PctR_N, 2),
         PctR_Round_H = round(PctR_H, 2)
        ) 

# str(TravDistMiH_Ntile)
# View(head(TravDistMiH_Ntile, 500))

TravDistMiH_Ntile_Rows <- nrow(TravDistMiH_Ntile)

# View(tail(TravDistMiH_Ntile, 500))


TravDistMiH_Pctiles <- group_by(TravDistMiH_Ntile,
                                PctR_Round_H
                               ) %>% 
  summarise(
    # MinTDMiAtPctile_N = min(TravelDistance_Mi_New),
    MinTDMiAtPctile_H = min(TravelDistance_Mi_NewHvrs),
    # CntsAtPctile_N = sum(!is.na(TravelDistance_Mi_New)),
    CntsAtPctile_H = sum(!is.na(TravelDistance_Mi_NewHvrs)),
    # PctsAtPctile_N = CntsAtPctile_N / TravDistMiH_Ntile_Rows,
    PctsAtPctile_H = CntsAtPctile_H / TravDistMiH_Ntile_Rows
  ) %>% 
  mutate(# CumSumPAtP_N = cumsum(PctsAtPctile_N),
         CumSumPAtP_H = cumsum(PctsAtPctile_H)
        )

# View(TravDistMiH_Pctiles)
TravDistMiH_Pctiles

Join TravDistMiH_Pctiles, TravDistMiN_Pctiles, and TravDistMi_Pctiles.

~11% of rides are still showing as less than 0.1 miles of TravelDistance_Mi_NewHvrs.


rm(TravDistMiN_Ntile_Rows, TravDistMiH_Ntile_Rows, TravDistMiN_Ntile, TravDistMiH_Ntile)


# View(TravDistMi_Pctiles)
# View(TravDistMiN_Pctiles)
# View(TravDistMiH_Pctiles)

TravDistMi_Pctiles_All <- inner_join(x = TravDistMi_Pctiles,
                                     y = TravDistMiN_Pctiles,
                                     by = c("PctR_Round" = "PctR_Round_N")
                                    ) %>% 
  inner_join(y = TravDistMiH_Pctiles,
             by = c("PctR_Round" = "PctR_Round_H")
            ) %>% 
  select(PctR_Round,
         MinTravDistMiAtPctile,
         MinTDMiAtPctile_N,
         MinTDMiAtPctile_H,
         CntsAtPctile,
         CntsAtPctile_N,
         CntsAtPctile_H,
         PctsAtPctile,
         PctsAtPctile_N,
         PctsAtPctile_H,
         CumSumPAtP,
         CumSumPAtP_N,
         CumSumPAtP_H
         )

# str(TravDistMi_Pctiles_All)

rm(TravDistMi_Pctiles, TravDistMiN_Pctiles,TravDistMiH_Pctiles)


View(TravDistMi_Pctiles_All)
TravDistMi_Pctiles_All

Investigation of TravelDistance_Mi_New.

Why are there still some small or large TravelDistance_Mi_NewHvrs values.


# View(filter(AllDays_NewTravelDist,
#             !is.na(TravelDistance_Mi_NewHvrs)
#            ) %>% 
#        select(-TD_Mi_q2,
#               -TD_Mi_q98,
#               -TD_Mi_SS_q5,
#               -TD_Mi_SS_q95,
#               -TD_Mi_SSHG_q5,
#               -TD_Mi_SSHG_q95,
#               -TD_Mi_Mean,
#               -TD_Mi_Mean_F,
#               -TD_Mi_SS_Mean,
#               -TD_Mi_SS_Mean_F,
#               -TD_Mi_SSHG_Mean,
#               -TD_Mi_SSHG_Mean_F,
#               -TD_Mi_Med,
#               -TD_Mi_Med_F,
#               -TD_Mi_SS_Med,
#               -TD_Mi_SS_Med_F,
#               -TD_Mi_SSHG_Med,
#               -TD_Mi_SSHG_Med_F,
#               -TD_Mi_Cnt,
#               -TD_Mi_Cnt_F,
#               -TD_Mi_SS_Cnt,
#               -TD_Mi_SS_Cnt_F,
#               -TD_Mi_SSHG_Cnt,
#               -TD_Mi_SSHG_Cnt_F,
#               -TT_Sec_q2,
#               -TT_Sec_q98,
#               -TT_Sec_SS_q5,
#               -TT_Sec_SS_q95,
#               -TT_Sec_SSHG_q5,
#               -TT_Sec_SSHG_q95,
#               -TT_Sec_Mean,
#               -TT_Sec_Mean_F,
#               -TT_Sec_SS_Mean,
#               -TT_Sec_SS_Mean_F,
#               -TT_Sec_SSHG_Mean,
#               -TT_Sec_SSHG_Mean_F,
#               -TT_Sec_Med,
#               -TT_Sec_Med_F,
#               -TT_Sec_SS_Med,
#               -TT_Sec_SS_Med_F,
#               -TT_Sec_SSHG_Med,
#               -TT_Sec_SSHG_Med_F,
#               -TT_Sec_Cnt,
#               -TT_Sec_Cnt_F,
#               -TT_Sec_SS_Cnt,
#               -TT_Sec_SS_Cnt_F,
#               -TT_Sec_SSHG_Cnt,
#               -TT_Sec_SSHG_Cnt_F,
#               -TT_Hr_q2,
#               -TT_Hr_q98,
#               -TT_Hr_SS_q5,
#               -TT_Hr_SS_q95,
#               -TT_Hr_SSHG_q5,
#               -TT_Hr_SSHG_q95,
#               -TT_Hr_Mean,
#               -TT_Hr_Mean_F,
#               -TT_Hr_SS_Mean,
#               -TT_Hr_SS_Mean_F,
#               -TT_Hr_SSHG_Mean,
#               -TT_Hr_SSHG_Mean_F,
#               -TT_Hr_Med,
#               -TT_Hr_Med_F,
#               -TT_Hr_SS_Med,
#               -TT_Hr_SS_Med_F,
#               -TT_Hr_SSHG_Med,
#               -TT_Hr_SSHG_Med_F,
#               -TT_Hr_Cnt,
#               -TT_Hr_Cnt_F,
#               -TT_Hr_SS_Cnt,
#               -TT_Hr_SS_Cnt_F,
#               -TT_Hr_SSHG_Cnt,
#               -TT_Hr_SSHG_Cnt_F
#              ) %>% 
#        arrange(TravelDistance_Mi_NewHvrs) %>% 
#        head(500)
#     )

View(filter(AllDays_NewTravelDist,
            !is.na(TravelDistance_Mi_NewHvrs)
           ) %>% 
       select(-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
             ) %>% 
       arrange(TravelDistance_Mi_NewHvrs) %>%
       head(500)
    )

# examples of the smallest TravelDistance_Mi_NewHvrs values.
View(filter(AllDays_NewTravelDist,
            (RowNum_OG >= 1424440 & RowNum_OG <= 1424460) | # 1424450  --  direction change
                (RowNum_OG >= 763292 & RowNum_OG <= 763312) | # 763302  --  direction change
                (RowNum_OG >= 1679093 & RowNum_OG <= 1679113) | # 1679103  --  direction change
                (RowNum_OG >= 2860918 & RowNum_OG <= 2860938) # 2860928  --  looks correct
           ) %>% 
       select(-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
             )
    )


View(filter(AllDays_NewTravelDist,
            !is.na(TravelDistance_Mi_NewHvrs)
           ) %>% 
       select(-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
             ) %>% 
       arrange(desc(TravelDistance_Mi_NewHvrs)
              ) %>%
       head(500)
    )

# examples of the largest TravelDistance_Mi_NewHvrs values.
View(filter(AllDays_NewTravelDist,
            (RowNum_OG >= 1092000 & RowNum_OG <= 1092050) | # 1092030  --  direction change
                (RowNum_OG >= 1609460 & RowNum_OG <= 1609480) | # 1609470  -- direction change 
                (RowNum_OG >= 508904 & RowNum_OG <= 508924) | # 508914  --  direction change & original StopID was bad
                (RowNum_OG >= 2476345 & RowNum_OG <= 2476365) # 2476355  --  direction change
           ) %>% 
       select(-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
             )
    )

Investigation of TravelTime_Hr.

View(TravDistMi_Pctiles): 98% of TravelTime_Hr are between 7 seconds and 464 seconds (~8 minutes).


TravTimeHr_Ntile <- select(AllDays_NewTravelDist,
                           TravelTime_Hr
                          ) %>% 
  mutate(# Pctile = ntile(AllDays_NewTravelDist$TravelTime_Hr, 100),
         # MinR = min_rank(AllDays_NewTravelDist$TravelTime_Hr),
         PctR = percent_rank(AllDays_NewTravelDist$TravelTime_Hr),
         PctR_Round = round(PctR, 2)
        ) 

# str(TravTimeHr_Ntile)

TravTimeHr_Ntile_Rows <- nrow(TravTimeHr_Ntile)

# View(tail(TravTimeHr_Ntile, 500))


TravTimeHr_Pctiles <- group_by(TravTimeHr_Ntile,
                               PctR_Round
                              ) %>% 
  summarise(
    MinTravTimeHrAtPctile = min(TravelTime_Hr),
    CntsAtPctile = n(),
    PctsAtPctile = CntsAtPctile / TravTimeHr_Ntile_Rows
  ) %>% 
  mutate(CumSumPAtP = cumsum(PctsAtPctile),
         MinTravTimeSecAtPctile = MinTravTimeHrAtPctile * 3600
        )

rm(TravTimeHr_Ntile_Rows)
rm(TravTimeHr_Ntile)
View(TravTimeHr_Pctiles)
TravTimeHr_Pctiles

Investigation of TravelTime_Hr.

Histogram of TravelTime_Sec.


TravTime_Sec_HistDen <- ggplot(filter(select(AllDays_NewTravelDist,
                                             TravelTime_Sec
                                            ),
                                      !is.na(TravelTime_Sec)
                                     ),
                               aes(x = TravelTime_Sec,
                                   y = ..density..
                                  )
                          ) +
  geom_histogram(binwidth = 5, fill = "lightblue", colour = "grey60", size = 0.2) +
  geom_line(stat = "density", colour = "red") +
  # stat_bin(binwidth = 5,
  #          geom = "text",
  #          size = 2.5,
  #          vjust = 1.5,
  #          aes(label = format(..count.., big.mark = ",")
  #             ),
  #         ) +
  coord_cartesian(xlim = c(0, 180), ylim = c(0, 0.02)
                 ) +
  #  theme(legend.position="none") +
  labs(title = "Variation in Travel Time",
       x = "Travel Time (sec)",
       y = "Density"
      )

TravTime_Sec_HistDen

Investigation of TravelTime_Sec.

TravelTime_Sec values are NA.


summary(AllDays_NewTravelDist$TravelTime_Sec)


View(select(AllDays_NewTravelDist,
            -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
           ) %>% 
       filter(is.na(TravelTime_Sec) &
                BusDay_EventNum != 1  # TravelTime purposefully not calculated here
             )
    )

# examples of TravelTime_Sec values that are NA. These are NA because the Event_Time & Departure_Time readings are not accurate (i.e., the previous Departure_Time is BEFORE or EQUAL TO the current Event_Time).
View(filter(AllDays_NewTravelDist,
            (RowNum_OG >= 90809 & RowNum_OG <= 90829) | # 90819
                (RowNum_OG >= 90881 & RowNum_OG <= 90901) | # 90891
                (RowNum_OG >= 2597066 & RowNum_OG <= 2597086) | # 2597076
                (RowNum_OG >= 2613305 & RowNum_OG <= 2613325) # 2613315
           ) %>% 
       select(-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt"))
    )

Investigation of TravelTime_Sec.

TravelTime_Sec values are extremely small.


View(select(AllDays_NewTravelDist,
            -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
           ) %>% 
       filter(!is.na(TravelTime_Sec)
             ) %>% 
       arrange(TravelTime_Sec,
               desc(SpeedAvg_Mph_NewHvrs)
              ) %>%
       head(500)
    )

# examples where TravelTime_Sec is small (1 sec) and SpeedAvg_Mph_NewHvrs is large.
View(select(AllDays_NewTravelDist,
            -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
           ) %>% 
       filter((RowNum_OG >= 2217353 & RowNum_OG <= 2217373) | # 2217363
                (RowNum_OG >= 3090321 & RowNum_OG <= 3090341) | # 3090331
                (RowNum_OG >= 80764 & RowNum_OG <= 80784) | # 80774
                (RowNum_OG >= 33840 & RowNum_OG <= 33860) # 33850
           )
    )

Investigation of TravelTime_Sec.

TravelTime_Sec values are extremely large.


View(select(AllDays_NewTravelDist,
            -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
           ) %>% 
       filter(!is.na(TravelTime_Sec)
             ) %>% 
       arrange(desc(TravelTime_Sec),
               SpeedAvg_Mph_NewHvrs
              ) %>%
       head(500)
    )

# examples where TravelTime_Sec is large and SpeedAvg_Mph_NewHvrs is small.
View(select(AllDays_NewTravelDist,
            -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
           ) %>% 
       filter((RowNum_OG >= 1007703 & RowNum_OG <= 1007723) | # 1007713
                (RowNum_OG >= 2373564 & RowNum_OG <= 2373584) | # 2373574
                (RowNum_OG >= 864379 & RowNum_OG <= 864399) | # 864389
                (RowNum_OG >= 2570060 & RowNum_OG <= 2570080) # 2570070
           )
    )

Investigation of TravelTime_Sec.

Are large TravelTime_Sec values related to RouteChanges? Looks likely. When the Bus involves a Route “change”, there is almost twice as likely to be a case of an outlier TravelTime_Sec value (on the high side).


TTLargeRteChng <- select(AllDays_NewTravelDist,
                         -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
                        ) %>% 
  mutate(TT_Out = factor(ifelse(TravelTime_Sec > 464,  # this is the 99th percentile
                                "Outlier",
                                "Normal"
                               )
                        )
        )

# str(TTLargeRteChng)


TTLargeRteChng_Cnts <- group_by(TTLargeRteChng,
                                RteChange2,
                                TT_Out
                               ) %>% 
  summarise(Cnts = n()
           )

TTLargeRteChng_Spread <- as.data.frame(spread(TTLargeRteChng_Cnts,
                                              TT_Out,
                                              Cnts
                                             )
                                      ) %>%
  select(-RteChange2)

row.names(TTLargeRteChng_Spread) <- c("Change", "Same")
# str(TTLargeRteChng_Spread)


# When the Bus involves a Route "change", there is almost twice as likely to be a case of an outlier TravelTime_Sec value.
TTLargeRteChng_Spread
prop.table(as.table(as.matrix(TTLargeRteChng_Spread)
                   ),
           1
          )

prop.table(as.table(as.matrix(TTLargeRteChng_Spread)
                   ),
           2
          )

# rm(TTLargeRteChng, TTLargeRteChng_Spread)
         

Investigation of TravelTime_Sec.

Are large TravelTime_Sec values related to RouteChanges? Looks likely.


View(filter(TTLargeRteChng,
            !is.na(TravelTime_Sec) &
              RteChange2 == "Same"
           ) %>% 
       arrange(desc(TravelTime_Sec),
               SpeedAvg_Mph_NewHvrs
              ) %>%
       head(500)
    )


# examples where TravelTime_Sec is large and SpeedAvg_Mph_NewHvrs is small.
View(filter(TTLargeRteChng,
            (RowNum_OG >= 2250290 & RowNum_OG <= 2250310) | # 2250300
              (RowNum_OG >= 867717 & RowNum_OG <= 867737) | # 867727
              (RowNum_OG >= 864379 & RowNum_OG <= 864399) | # 864389
              (RowNum_OG >= 808395 & RowNum_OG <= 808415) # 808405
           )
    )

Investigation of TravelTime_Sec.

If TravelTime_Sec is below the 5th percentile for that StartStop_ID, or if TravelTime_Sec is above the 95th percentile for that StartStop_ID, consider this an outlier. In this case, replace the value with the mean for that StartStop_ID and HourGroup (TT_Sec_SSHG_Mean_F), or if there are not enough values at the HourGroup level, replace it with the mean for that StartStop_ID.


rm(TTLargeRteChng, TTLargeRteChng_Cnts, TTLargeRteChng_Spread)


NewTravTime <- mutate(AllDays_NewTravelDist,
                      TT_Sec_New = ifelse(!is.na(TravelTime_Sec) &
                                            (TravelTime_Sec < TT_Sec_SSHG_q5 |
                                               TravelTime_Sec > TT_Sec_SSHG_q95
                                            ) &
                                            TT_Sec_SSHG_Cnt_F >= 20,
                                          TT_Sec_SSHG_Mean_F,
                                   ifelse(!is.na(TravelTime_Sec) &
                                            (TravelTime_Sec < TT_Sec_SSHG_q5 |
                                               TravelTime_Sec > TT_Sec_SSHG_q95
                                            ) &
                                            TT_Sec_SSHG_Cnt_F < 20 &
                                            TT_Sec_SS_Cnt_F >= 20,
                                          TT_Sec_SS_Mean_F,
                                   ifelse(!is.na(TravelTime_Sec) &
                                            (TravelTime_Sec < TT_Sec_SSHG_q5 |
                                               TravelTime_Sec > TT_Sec_SSHG_q95
                                            ) &
                                            TT_Sec_SS_Cnt_F < 20 &
                                            TT_Sec_SS_Cnt >= 20,
                                          TT_Sec_SS_Mean,
                                   ifelse(!is.na(TravelTime_Sec) &
                                            (TravelTime_Sec < TT_Sec_SSHG_q5 |
                                               TravelTime_Sec > TT_Sec_SSHG_q95
                                            ) &
                                            TT_Sec_SS_Cnt_F < 20 &
                                            TT_Sec_SS_Cnt < 20 &
                                            RteChange2 == "Change",
                                          NA,
                                          TravelTime_Sec
                                         )))),
                      
                      TT_Sec_New_Label = 
           factor(ifelse(!is.na(TravelTime_Sec) &
                           (TravelTime_Sec < TT_Sec_SSHG_q5 |
                              TravelTime_Sec > TT_Sec_SSHG_q95
                           ) &
                           TT_Sec_SSHG_Cnt_F >= 20,
                         "TT_Sec_SSHG_Mean_F",
                  ifelse(!is.na(TravelTime_Sec) &
                           (TravelTime_Sec < TT_Sec_SSHG_q5 |
                              TravelTime_Sec > TT_Sec_SSHG_q95
                           ) &
                           TT_Sec_SSHG_Cnt_F < 20 &
                           TT_Sec_SS_Cnt_F >= 20,
                         "TT_Sec_SS_Mean_F",
                  ifelse(!is.na(TravelTime_Sec) &
                           (TravelTime_Sec < TT_Sec_SSHG_q5 |
                              TravelTime_Sec > TT_Sec_SSHG_q95
                            ) &
                           TT_Sec_SS_Cnt_F < 20 &
                           TT_Sec_SS_Cnt >= 20,
                         "TT_Sec_SS_Mean",
                  ifelse(!is.na(TravelTime_Sec) &
                           (TravelTime_Sec < TT_Sec_SSHG_q5 |
                              TravelTime_Sec > TT_Sec_SSHG_q95
                           ) &
                           TT_Sec_SS_Cnt_F < 20 &
                           TT_Sec_SS_Cnt < 20 &
                           RteChange2 == "Change",
                         NA,
                         "TravelTime_Sec"
                        ))))
                 ),
                  
                  TT_Hr_New = TT_Sec_New / (60 * 60)
           )


dim(AllDays_NewTravelDist)
dim(NewTravTime)
rm(AllDays_NewTravelDist)

summary(select(NewTravTime,
           -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
          )
   )

str(select(NewTravTime,
           TravelTime_Sec,
           TT_Sec_New,
           TT_Sec_New_Label,
           TT_Hr_New
          )
   )


summary(select(NewTravTime,
               TravelTime_Sec,
               TT_Sec_New,
               TT_Sec_New_Label,
               TT_Hr_New
              )
       )

Test investigation of just the X2 Route. Box plots for time between bus arrivals (by HourGroup).


View(head(select(NewTravTime,
                 -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
                )
         )
    )

X2 <- select(NewTravTime,
             -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
            ) %>% 
  filter(Route == "X2")

str(X2)

View(head(arrange(X2,
                  Bus_ID,
                  Event_Time
                 ),
          500
         )
    )

X2_ByStop <- group_by(X2,
                      StopID_Clean
                     ) %>% 
  arrange(StopID_Clean,
          Event_Time) %>% 
  mutate(Event_Time_L1 = lag(Event_Time),
         TimeToEvent_Sec = as.numeric(Event_Time - Event_Time_L1),
         TimeToEvent_Min = TimeToEvent_Sec / 60
        )

View(head(X2_ByStop, 500))


# Count_Values is needed to display the medians on the box plots
Count_Values <- ddply(as.data.frame(X2_ByStop),
                      .(Event_Time_HrGroup),
                      summarise,
                      Value_Counts = median(TimeToEvent_Min, na.rm = TRUE)
                     )

TimeBtwEvents_X2_BoxPlot <- ggplot(select(as.data.frame(X2_ByStop),
                                          TimeToEvent_Min,
                                          Event_Time_HrGroup
                                         ),
                                   aes(factor(Event_Time_HrGroup),
                                       TimeToEvent_Min,
                                       fill = factor(Event_Time_HrGroup)
                                      )
                                  ) + 
  geom_boxplot(outlier.colour="red", notch=TRUE, na.rm = TRUE) +
  geom_text(data = Count_Values,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 3,
            vjust = -0.5
           ) +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  coord_cartesian(# xlim = c(0, 180),
                  ylim = c(0, 120)
                 ) +
  labs(title = "How Often an X2 Arrives at a Given Stop",
       x = "Hour Group",
       y = "Time Between Busses (min)"
      )

TimeBtwEvents_X2_BoxPlot

Test investigation of just the X2 Route. Violin plots for time between bus arrivals (by Hour Group).


TimeBtwEvents_X2_ViolinPlot <- ggplot(select(as.data.frame(X2_ByStop),
                                             TimeToEvent_Min,
                                             Event_Time_HrGroup
                                             ),
                                      aes(factor(Event_Time_HrGroup),
                                          TimeToEvent_Min,
                                          fill = factor(Event_Time_HrGroup)
                                         )
                                     ) + 
  geom_violin(draw_quantiles = c(0.25, 0.5, 0.75),
              trim = TRUE,
              scale = "count",
              na.rm = TRUE,
              show.legend = NA,
              inherit.aes = TRUE
             ) +
  geom_text(data = Count_Values,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 2.5,
            vjust = -0.5
           ) +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  coord_cartesian(# xlim = c(0, 180),
                  ylim = c(0, 80)
                 ) +
  labs(title = "How Often an X2 Arrives at a Given Stop",
       x = "Hour Group",
       y = "Time Between Busses (min)"
      )

TimeBtwEvents_X2_ViolinPlot

Test investigation of just the X2 Route. Box plots for time between bus arrivals (by Zip Code).


# Count_Values is needed to display the medians on the box plots
Count_Values_z <- ddply(as.data.frame(X2_ByStop),
                        .(Stop_Zip),
                        summarise,
                        Value_Counts = median(TimeToEvent_Min, na.rm = TRUE)
                       )

TimeBtwEvents_X2_BoxPlot_z <- ggplot(select(as.data.frame(X2_ByStop),
                                            TimeToEvent_Min,
                                            Stop_Zip
                                           ),
                                     aes(factor(Stop_Zip),
                                         TimeToEvent_Min,
                                         fill = factor(Stop_Zip)
                                        )
                                    ) + 
  geom_boxplot(outlier.colour="red", notch=TRUE, na.rm = TRUE) +
  geom_text(data = Count_Values_z,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 3,
            vjust = -0.5
           ) +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  coord_cartesian(# xlim = c(0, 180),
                  ylim = c(0, 100)
                 ) +
  labs(title = "How Often an X2 Arrives at a Given Stop",
       x = "Zip Code of Destination",
       y = "Time Between Busses (min)"
      )

TimeBtwEvents_X2_BoxPlot_z

Test investigation of just the X2 Route. Violin plots for time between bus arrivals (by Zip Code).


TimeBtwEvents_X2_ViolinPlot_z <- ggplot(select(as.data.frame(X2_ByStop),
                                               TimeToEvent_Min,
                                               Stop_Zip
                                               ),
                                        aes(factor(Stop_Zip),
                                            TimeToEvent_Min,
                                            fill = factor(Stop_Zip)
                                           )
                                       ) + 
  geom_violin(draw_quantiles = c(0.25, 0.5, 0.75),
              trim = TRUE,
              scale = "count",
              na.rm = TRUE,
              show.legend = NA,
              inherit.aes = TRUE
             ) +
  geom_text(data = Count_Values_z,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 2.5,
            vjust = -0.5
           ) +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  coord_cartesian(# xlim = c(0, 180),
                  ylim = c(0, 60)
                 ) +
  labs(title = "How Often an X2 Arrives at a Given Stop",
       x = "Zip Code of Destination",
       y = "Time Between Busses (min)"
      )

TimeBtwEvents_X2_ViolinPlot_z

Waiting time analyses.

Munging and sampling data to go from time beteen buses to “average” waiting time.

First, get the max and min times of bus stops (each day, and for each route).


rm(X2, X2_ByStop, X2_Long, X2_Pct)


RouteMinMax <- group_by(NewTravTime,
                        Route,
                        Event_Time_Date
                       ) %>% 
  summarise(MinTime = min(Event_Time),
            MaxTime = max(Event_Time)
           )

str(RouteMinMax)
View(RouteMinMax)

Waiting time analyses.

Munging and sampling data to go from time beteen buses to “average” waiting time.

(Pulls here are done by day, as the data are too large to do at once.)


# View(head(NewTravTime, 500))

# For each record, create a random datetime between the first and last stop for that bus route (on that day).
for(i in 3:7){

set.seed(123456789)
Samp <- select(NewTravTime,
               RowNum_OG,
               Route,
               # RouteGroup,
               Event_Time_Date,
               StopID_Clean,
               starts_with("Event")
              ) %>% 
  filter(Event_Time_Date == i) %>%  # needed to do this each day (3-7) because the complete file was too large to do at once
  left_join(RouteMinMax,
            by = c("Route" = "Route",
                   "Event_Time_Date" = "Event_Time_Date"
                  )
           ) %>% 
  mutate(SampTime = as_datetime(runif(nrow(.), #200000,
                                      min = MinTime,
                                      max = MaxTime
                                     ),
                                tz = "America/New_York"
                               )
        ) %>% 
  arrange(Route,
          StopID_Clean,
          Event_Time
         ) 

# str(Samp)
# View(head(Samp, 500))
# 
# View(
# group_by(Samp,
#          RowNum_OG
#         ) %>%
#   summarise(Cnt_Num = n(),
#             Cnt_Pct = 100 * Cnt_Num / nrow(Samp)
#            ) %>%
#   arrange(desc(Cnt_Num))
# )


# For each Route and StopID combination, get all the Event_Time values that are after the SampTime value.
# estimating approx 2hrs of runtime for all 2.8m records
Testing_A <- sqldf("   Select               t1.*
                                            ,t2.Event_Time             as NextBus
                        From                 Samp                      as t1
                             Inner Join      Samp                      as t2
                                On              t1.Route = t2.Route
                                And             t1.StopID_Clean = t2.StopID_Clean
                                And             t2.Event_Time > t1.SampTime
                        Order By             t1.Route
                                            ,t1.StopID_Clean
                                            ,t1.Event_Time
                                            ,t2.Event_Time
                  "
                 ) %>% 
  mutate(NB = as_datetime(NextBus,
                          tz = "America/New_York"
                         )
        )

# str(Testing_A)
# View(head(Testing_A, 500))
# View(head(Samp, 500))


# Filter the dataframe to only include the bus arrival at StopID that is the next to come after the SampTime.
# estimating approx 20min of runtime for all 2.8m records
Testing <- select(Testing_A,
                  -NextBus
                 ) %>% 
  group_by(RowNum_OG) %>% 
  filter(NB == min(NB)
        ) %>% 
  arrange(Route,
          StopID_Clean,
          Event_Time
         ) %>% 
  mutate(WaitTime_Min = as.numeric(NB - SampTime),
         WaitTime_Sec = WaitTime_Min * 60,
         WaitTime_Sec2 = NB - SampTime,
         WaitTime_Min2 = WaitTime_Sec2 / 60
        ) %>% 
  as.data.frame()

assign(paste0("Testing_", i),
       Testing
      )

rm(Samp,Testing_A, Testing)
str(get(paste0("Testing_", i)))
View(get(paste0("Testing_", i)))
}


# Bind all the individual dataframes together.
WaitData_DayPull <- bind_rows(Testing_3,
                              Testing_4,
                              Testing_5,
                              Testing_6,
                              Testing_7
                             ) %>% 
  mutate(WaitTime_Sec3 = NB - SampTime,
         WaitTime_Min3 = WaitTime_Sec3 / 60
        ) %>% 
  arrange(Route,
          StopID_Clean,
          Event_Time
         )

rm(Testing_3, Testing_4, Testing_5, Testing_6, Testing_7)
str(WaitData_DayPull)
View(head(WaitData_DayPull, 500))
View(tail(WaitData_DayPull, 500))

Waiting time analyses.

Munging and sampling data to go from time beteen buses to “average” waiting time.

Basic investigation of any missing rows from data pulled by day.


DistinctRowNum_OG <- distinct(select(WaitData_DayPull,
                                     RowNum_OG
                                    )
                             )

str(DistinctRowNum_OG)

# View(
# anti_join(Samp,
#           DistinctRowNum_OG,
#           by = c("RowNum_OG" = "RowNum_OG")
#          )
# )


# The samp time is AFTER the last bus passed that StopID_Clean
# View(filter(Samp,
#             Event_Time > "2016-10-07 19:48:41" &
#               Route == "X2" &
#               StopID_Clean == 1003774
#            )
#     )

# Next Bus (NB) can be on the next morning
# View(filter(Testing7,
#             SampTime > "2016-10-06 23:58:00" &
#               SampTime < "2016-10-06 23:59:59")
#     )

Waiting time analyses.

Munging and sampling data to go from time beteen buses to “average” waiting time.

(Pulls here are done by groupings of bus routes, as the data are too large to do at once.)

First, we need to find the most common bus routes.


rm(DistinctRowNum_OG)


# View(head(NewTravTime, 500))

set.seed(123456789)
BusGroups <- group_by(NewTravTime,
                      Route
                     ) %>% 
  summarise(Cnt_Num = n(),
            Cnt_Pct = Cnt_Num / nrow(NewTravTime)
           ) %>% 
  arrange(desc(Cnt_Num)
         ) %>% 
  mutate(RowNum = row_number(),
         RandNum = runif(n = 268),
         RouteGroup = ifelse(RandNum <= 0.2,
                             1,
                      ifelse(RandNum <= 0.4,
                             2,
                      ifelse(RandNum <= 0.6,
                             3,
                      ifelse(RandNum <= 0.8,
                             4,
                             5
                            ))))
        )

str(BusGroups)
View(BusGroups)
summary(BusGroups)

Waiting time analyses.

Munging and sampling data to go from time beteen buses to “average” waiting time.

(Pulls here are done by groupings of bus routes, as the data are too large to do at once.)


# View(head(NewTravTime, 500))

# For each record, create a random datetime between the first and last stop for that bus route (on that day).
for(i in 1:5){
  
set.seed(123456789)
Samp <- left_join(NewTravTime,
                  BusGroups,
                  by = c("Route" = "Route")
                  ) %>% 
  select(RowNum_OG,
         Route,
         RouteGroup,
         Event_Time_Date,
         StopID_Clean,
         starts_with("Event")
        ) %>% 
  filter(RouteGroup == i) %>%  # needed to do this each RouteGroup (1-5) because the complete file was too large to do at once
  left_join(RouteMinMax,
            by = c("Route" = "Route",
                   "Event_Time_Date" = "Event_Time_Date"
                  )
           ) %>% 
  mutate(SampTime = as_datetime(runif(nrow(.), #200000,
                                      min = MinTime,
                                      max = MaxTime
                                     ),
                                tz = "America/New_York"
                               )
        ) %>% 
  arrange(Route,
          StopID_Clean,
          Event_Time
         ) 

# str(Samp)
# View(head(Samp, 500))
# 
# View(
# group_by(Samp,
#          RowNum_OG
#         ) %>%
#   summarise(Cnt_Num = n(),
#             Cnt_Pct = 100 * Cnt_Num / nrow(Samp)
#            ) %>%
#   arrange(desc(Cnt_Num))
# )


# For each Route and StopID combination, get all the Event_Time values that are after the SampTime value.
# estimating approx 2hrs of runtime for all 2.8m records
Testing_A <- sqldf("   Select               t1.*
                                            ,t2.Event_Time             as NextBus
                        From                 Samp                      as t1
                             Inner Join      Samp                      as t2
                                On              t1.Route = t2.Route
                                And             t1.StopID_Clean = t2.StopID_Clean
                                And             t2.Event_Time > t1.SampTime
                        Order By             t1.Route
                                            ,t1.StopID_Clean
                                            ,t1.Event_Time
                                            ,t2.Event_Time
                  "
                 ) %>% 
  mutate(NB = as_datetime(NextBus,
                          tz = "America/New_York"
                         )
        )

# str(Testing_A)
# View(head(Testing_A, 500))
# View(head(Samp, 500))


# Filter the dataframe to only include the bus arrival at StopID that is the next to come after the SampTime.
# estimating approx 20min of runtime for all 2.8m records
Testing <- select(Testing_A,
                  -NextBus
                 ) %>% 
  group_by(RowNum_OG) %>% 
  filter(NB == min(NB)
        ) %>% 
  arrange(Route,
          StopID_Clean,
          Event_Time
         ) %>% 
  mutate(WaitTime_Min = as.numeric(NB - SampTime),
         WaitTime_Sec = WaitTime_Min * 60
        ) %>% 
  as.data.frame()

assign(paste0("Testing", i),
       Testing
      )

rm(Samp,Testing_A, Testing)
str(get(paste0("Testing", i)))
View(get(paste0("Testing", i)))
}


# Bind all the individual dataframes together.
WaitData_RoutePull <- bind_rows(Testing1,
                                Testing2,
                                Testing3,
                                Testing4,
                                Testing5
                             ) %>% 
  mutate(WaitTime_Sec2 = NB - SampTime,
         WaitTime_Min2 = WaitTime_Sec2 / 60
        ) %>% 
  arrange(Route,
          StopID_Clean,
          Event_Time
         )

rm(BusGroups, i, Testing3, Testing4, Testing5, Testing6, Testing7)
str(WaitData_RoutePull)
View(head(WaitData_RoutePull, 500))
View(tail(WaitData_RoutePull, 500))

Waiting time analyses.

Munging and sampling data to go from time beteen buses to “average” waiting time.

Compare WaitData pulled by day and pulled by route.


dim(WaitData_RoutePull)
dim(WaitData_DayPull)
nrow(WaitData_RoutePull) - nrow(WaitData_DayPull)

WaitData_Diff <- anti_join(WaitData_RoutePull,
                           WaitData_DayPull,
                           by = c("RowNum_OG" = "RowNum_OG"
                                 )
                          ) %>% 
  select(-WaitTime_Min,
         -WaitTime_Sec
        )

str(WaitData_Diff)
View(head(WaitData_Diff, 500))

View(filter(WaitData_RoutePull,
            Route == "Z8" &
              StopID_Clean == 2005465
            # RowNum_OG = 2902760
            # Event_Time = 2016-10-07 19:51:47
           )
    )

View(group_by(WaitData_Diff,
              Route
             ) %>% 
       summarise(Cnt_Num = n(),
                 Cnt_Pct = Cnt_Num / nrow(WaitData_Diff)
                ) %>% 
       arrange(desc(Cnt_Num)
              )
    )

View(filter(WaitData_Diff,
            Route == "S1"
           )
    )

View(filter(WaitData_RoutePull,
            Route == "S1" &
              StopID_Clean == 1003132
            # RowNum_OG = 1151770
            # Event_Time = 2016-10-07 09:07:12
           )
    )

# Can't tell why the pull by day has less records than the pull by route

Waiting time analyses.

Munging and sampling data to go from time beteen buses to “average” waiting time.

Compare WaitData (pulled by route) and original data (NewTravTime).


dim(NewTravTime)  # 2,809,529 rows
dim(WaitData_RoutePull)  # 2,780,848 rows
nrow(NewTravTime) - nrow(WaitData_RoutePull)  # is 28,681 rows

str(select(NewTravTime,
           -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
          )
   )
str(WaitData_RoutePull)

Compare_NTT_WD <- left_join(NewTravTime,
                            select(WaitData_RoutePull,
                                   RowNum_OG,
                                   # Route,
                                   RouteGroup,
                                   # StopID_Clean,
                                   # Event_Time,
                                   MinTime,
                                   MaxTime,
                                   SampTime,
                                   NB,
                                   WaitTime_Sec2,
                                   WaitTime_Min2
                                  ),
                            by = c("RowNum_OG" = "RowNum_OG")
                           ) %>% 
  select(-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
        ) %>% 
  arrange(Route,
          StopID_Clean,
          Event_Time
         )

str(Compare_NTT_WD)  # 2,810,109 rows overall  --  29,261 rows with no match
View(head(Compare_NTT_WD, 500))
View(filter(Compare_NTT_WD,
            is.na(MinTime)
           )
    )



# View(anti_join(Samp,
#                distinct(select(WaitData_RoutePull,
#                                RowNum_OG
#                               )
#                        ),
#                by = c("RowNum_OG" = "RowNum_OG")
#               )
#     )

# The SampTime is AFTER the last bus passed that StopID_Clean
# View(filter(Samp,
#               Route == "X2" &
#               StopID_Clean == 1003774
#             # RowNum_OG = 1146723
#             # Event_Time = 2016-10-07 15:32:18
#            )
#     )

Clean up the data a bit.


rm(BusGroups, RouteMinMax, Samp, Testing1, Testing2, Testing3, Testing4, Testing5, Testing_3, Testing_4, Testing_5, Testing_6, Testing_7, WaitData_DayPull, WaitData_Diff)


str(Compare_NTT_WD)
View(head(Compare_NTT_WD, 500))
View(head(mutate(Compare_NTT_WD,
                 WT_Min = as.numeric(WaitTime_Min2)
                )
         )
    )

WaitTime_AsNum <- Compare_NTT_WD %>% 
  mutate(RouteStop_ID = factor(paste(Route, StopID_Clean, sep = "__")
                              )
        )
WaitTime_AsNum$WaitTime_Sec2 <- as.numeric(WaitTime_AsNum$WaitTime_Sec2)
WaitTime_AsNum$WaitTime_Min2 <- as.numeric(WaitTime_AsNum$WaitTime_Min2)

rm(Compare_NTT_WD)
str(WaitTime_AsNum)

General exploration of wait times.


summary(WaitTime_AsNum$WaitTime_Min2)

General exploration of wait times.


WT_Quantiles <- as.data.frame(quantile(WaitTime_AsNum$WaitTime_Min2,
                                       probs = seq(0, 1, 0.01),
                                       na.rm = TRUE
                                      )
                             )

colnames(WT_Quantiles) <- "Value_Min"

WT_Quantiles$Value_Sec = format(round(WT_Quantiles$Value_Min * 60,
                                      digits = 2
                                     ),
                                nsmall = 2
                               )
WT_Quantiles$Value_Hr = format(round(WT_Quantiles$Value_Min / 60,
                                     digits = 2
                                    ),
                                nsmall = 2
                               )
WT_Quantiles$Value_Min = format(round(WT_Quantiles$Value_Min,
                                      digits = 2
                                     ),
                                nsmall = 2
                               )

WT_Quantiles$Quantile <- seq(0, 1, 0.01)

WT_Quantiles <- select(WT_Quantiles,
                       Quantile,
                       Value_Sec,
                       Value_Min,
                       Value_Hr
                      )

str(WT_Quantiles)
View(WT_Quantiles)
WT_Quantiles


View(arrange(WaitTime_AsNum,
             desc(WaitTime_Min2)
            ) %>% 
       head(., 5000)
    )

View(filter(WaitTime_AsNum,
            between(WaitTime_Min2, 60, 200)
           ) %>% 
       arrange(desc(WaitTime_Min2)
              ) 
     # %>% 
     #   head(., 5000)
    )

# Example of extreme wait times
View(filter(WaitTime_AsNum,
            Route == "W13" &  # only 2 bus passes in the entire dataset
              StopID_Clean == 1003728
            # Event_Time = 2016-10-03 08:42:46
           )
    )

# Example of extreme wait times
View(filter(WaitTime_AsNum,
            Route == "S41" &  # only 4 bus passes in the entire dataset
              StopID_Clean == 1001095
            # Event_Time = 2016-10-05 15:41:47
           )
    )

# Example of extreme wait times
View(filter(WaitTime_AsNum,
            Route == "D8" &  # route has VERY limited service after midnight
              StopID_Clean == 1001669
            # Event_Time = 2016-10-06 20:31:16
           )
    )

Looks like there might be an issue in wait times when very few Route-Stop combinations are included in the dataset. Let’s explore these.


RouteStop_Cnts <- group_by(WaitTime_AsNum,
                           RouteStop_ID
                          ) %>% 
  summarise(RouteStop_CntNum = n(),
            RouteStop_CntPct = RouteStop_CntNum / nrow(WaitTime_AsNum)
           ) %>% 
  arrange(RouteStop_CntNum)

View(RouteStop_Cnts)


RouteStop_CntOfCnt <- group_by(RouteStop_Cnts,
                               RouteStop_CntNum
                              ) %>% 
  summarise(RouteStopCnt_CntNum = n(),
            RouteStopCnt_CntPct = RouteStopCnt_CntNum / nrow(RouteStop_Cnts)
           ) %>% 
  mutate(RouteStopCnt_CntPct_CumSum = cumsum(RouteStopCnt_CntPct),
         x = 1 - RouteStopCnt_CntPct_CumSum
        ) %>% 
  arrange(RouteStop_CntNum)
  
 View(RouteStop_CntOfCnt)
 RouteStop_CntOfCnt

Histogram of the counts of Route-StopID combinations.


RouteStop_Cnts_Bar <- ggplot(RouteStop_CntOfCnt,
                             aes(x = RouteStop_CntNum,
                                 # y = ..density..
                                 y = RouteStopCnt_CntNum
                                )
                            ) +
  # geom_histogram(binwidth = 5, fill = "lightblue", colour = "grey60", size = 0.2) +
  geom_col(fill = "lightblue", colour = "grey60", size = 0.2) +
  coord_cartesian(xlim = c(0, 500)
                  # ylim = c(0, 0.02)
                 ) +
  labs(title = "Variation in Routes Passing a Specific Stop",
       x = "Occurrences of Route-StopID Combiantions",
       y = "Counts"
      )

RouteStop_Cnts_Bar

Create a new dataset limiting extremely small counts of Route-StopID combinations.


WaitTime_RteCnts <- left_join(WaitTime_AsNum,
                              RouteStop_Cnts,
                              by = c("RouteStop_ID" = "RouteStop_ID")
                             ) %>% 
  select(-RouteStop_CntPct)

dim(WaitTime_AsNum)
dim(WaitTime_RteCnts)

rm(WaitTime_AsNum)
str(WaitTime_RteCnts)


# Total rows
nrow(WaitTime_RteCnts)

# Rows of rare RouteStops
nrow(filter(WaitTime_RteCnts,
            RouteStop_CntNum <= 60
           )
    ) / nrow(WaitTime_RteCnts)

# Rows of extremely long wait times
nrow(filter(WaitTime_RteCnts,
            WaitTime_Min2 > 180
           )
    ) / nrow(WaitTime_RteCnts)


select(WaitTime_RteCnts,
       WaitTime_Min2
      ) %>% 
  summary()

filter(WaitTime_RteCnts,
       RouteStop_CntNum > 60  # 12 passes per day in a 5-day dataset
      ) %>% 
  select(WaitTime_Min2) %>% 
  summary()

filter(WaitTime_RteCnts,
       WaitTime_Min2 < 180  # probably means that something went wrong
      ) %>% 
  select(WaitTime_Min2) %>% 
  summary()

Compare quantiles in the limited datasets.


a <- as.data.frame(select(WaitTime_RteCnts,
                          WaitTime_Min2
                         ) %>% 
                     quantile(probs = seq(0, 1, 0.01), na.rm = TRUE)
                  )

b <- as.data.frame(filter(WaitTime_RteCnts,
                          RouteStop_CntNum > 60
                         ) %>% 
                     select(WaitTime_Min2) %>% 
                     quantile(probs = seq(0, 1, 0.01), na.rm = TRUE)
                  )

c <- as.data.frame(filter(WaitTime_RteCnts,
                          WaitTime_Min2 < 180
                         ) %>% 
                     select(WaitTime_Min2) %>% 
                     quantile(probs = seq(0, 1, 0.01), na.rm = TRUE)
                  )

WT_Filter_Quantiles <- bind_cols(a, b, c) %>% 
  mutate(Quantile = seq(0, 1, 0.01)
        )

colnames(WT_Filter_Quantiles) <- c("All", "RteStpAbv60", "WTBlw180", "Quantile")
rm(a, b, c)
View(WT_Filter_Quantiles)
WT_Filter_Quantiles

Histogram of all wait times.


WaitTime_AllBus_HistDen <- ggplot(filter(select(WaitTime_RteCnts,
                                                WaitTime_Min2
                                               ),
                                         !is.na(WaitTime_Min2)
                                        ),
                                  aes(x = WaitTime_Min2,
                                      y = ..density..
                                     )
                                ) +
  geom_histogram(binwidth = 5, fill = "lightblue", colour = "grey60", size = 0.2) +
  geom_line(stat = "density", colour = "red") +
  scale_x_continuous(breaks = seq(0, 300, 30)
                    ) +
  coord_cartesian(xlim = c(0, 300),
                  ylim = c(0, 0.035)
                 ) +
  labs(title = "Variation in Wait Time",
       x = "Wait Time (min)",
       y = "Density"
      )

WaitTime_AllBus_HistDen

Box plots for WaitTime (all busses, by Zip Code).


# Count_Values is needed to display the medians on the box plots
BusRoute <- select(WaitTime_RteCnts,
                   Route,
                   WaitTime_Min2,
                   Stop_Zip
                  ) %>% 
  filter(Route == "X2")

CountValues_AllBus_Zip <- ddply(BusRoute,
                                .(Stop_Zip),
                                summarise,
                                Value_Counts = median(WaitTime_Min2, na.rm = TRUE)
                               )

WaitTime_AllBus_Zip_Box <- ggplot(BusRoute,
                                  aes(factor(Stop_Zip),
                                      WaitTime_Min2,
                                      fill = factor(Stop_Zip)
                                     )
                                 ) + 
  geom_boxplot(outlier.colour="red", notch=TRUE, na.rm = TRUE) +
  geom_text(data = CountValues_AllBus_Zip,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 3,
            vjust = -0.5
           ) +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  coord_cartesian(# xlim = c(0, 180),
                  ylim = c(0, 45)
                 ) +
  labs(title = "Waiting Time at a Given Stop (for the X2)",
       x = "Zip Code of Destination",
       y = "Waiting Time (min)"
      )

WaitTime_AllBus_Zip_Box

Test investigation of just the X2 Route. Violin plots for time between bus arrivals (by Zip Code).


WaitTime_AllBus_Zip_Violin <- ggplot(BusRoute,
                                     aes(factor(Stop_Zip),
                                         WaitTime_Min2,
                                         fill = factor(Stop_Zip)
                                        )
                                    ) + 
  geom_violin(draw_quantiles = c(0.25, 0.5, 0.75),
              trim = TRUE,
              scale = "count",
              na.rm = TRUE,
              show.legend = NA,
              inherit.aes = TRUE
             ) +
  geom_text(data = CountValues_AllBus_Zip,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 3.5,
            vjust = -0.5
           ) +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  coord_cartesian(# xlim = c(0, 180),
                  ylim = c(0, 45)
                 ) +
  labs(title = "Waiting Time at a Given Stop (for the X2)",
       x = "Zip Code of Destination",
       y = "Waiting Time (min)"
      )

TimeBtwEvents_X2_ViolinPlot_z

Box plots for WaitTime (Zip Code, by HourGroupZip).


# Count_Values is needed to display the medians on the box plots
Zip <- select(WaitTime_RteCnts,
              Route,
              WaitTime_Min2,
              Stop_Zip,
              Event_Time_HrGroup
             ) %>% 
  filter(Stop_Zip == 20002)

CountValues_AllBus_HG <- ddply(Zip,
                               .(Event_Time_HrGroup),
                               summarise,
                               Value_Counts = median(WaitTime_Min2,
                                                     na.rm = TRUE
                                                    )
                               )

WaitTime_AllBus_HG_Box <- ggplot(Zip,
                                 aes(factor(Event_Time_HrGroup),
                                     WaitTime_Min2,
                                     fill = factor(Event_Time_HrGroup)
                                    )
                                ) + 
  geom_boxplot(outlier.colour="red", notch=TRUE, na.rm = TRUE) +
  geom_text(data = CountValues_AllBus_HG,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 2.5,
            vjust = -0.5
           ) +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  coord_cartesian(# xlim = c(0, 180),
                  ylim = c(0, 45)
                 ) +
  labs(title = "Waiting Time at a Given Stop (for Zip 20002)",
       x = "Hour Group",
       y = "Waiting Time (min)"
      )
  # facet_wrap(~Stop_Zip
  #            # nrow = 5
  #           )

WaitTime_AllBus_HG_Box

Violin plots for WaitTime (Zip Code, by HourGroupZip).


WaitTime_AllBus_HG_Vln <- ggplot(Zip,
                                 aes(factor(Event_Time_HrGroup),
                                     WaitTime_Min2,
                                     fill = factor(Event_Time_HrGroup)
                                    )
                                ) + 
  geom_violin(draw_quantiles = c(0.25, 0.5, 0.75),
              trim = TRUE,
              scale = "count",
              na.rm = TRUE,
              show.legend = NA,
              inherit.aes = TRUE
             ) +
  geom_text(data = CountValues_AllBus_HG,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 2.5,
            vjust = -0.5
           ) +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  coord_cartesian(# xlim = c(0, 180),
                  ylim = c(0, 90)
                 ) +
  labs(title = "Waiting Time at a Given Stop (for Zip 20002)",
       x = "Hour Group",
       y = "Waiting Time (min)"
      )
  # facet_wrap(~Stop_Zip
  #            # nrow = 5
  #           )

WaitTime_AllBus_HG_Vln

Box plots for WaitTime (Route, by HourGroupZip).


# Count_Values is needed to display the medians on the box plots
Rte <- select(WaitTime_RteCnts,
              Route,
              WaitTime_Min2,
              Stop_Zip,
              Event_Time_HrGroup
             ) %>% 
  filter(Route == "X2")

CountValues_AllBus_RteHG <- group_by(Rte,
                                     Event_Time_HrGroup
                                    ) %>% 
  summarise(
    Value_Counts = median(WaitTime_Min2,
                          na.rm = TRUE
                         ),
    VC = quantile(WaitTime_Min2, probs = 0.9, na.rm = TRUE)
    )


WaitTime_AllBus_RteHG_Box <- ggplot(Rte,
                                    aes(factor(Event_Time_HrGroup),
                                        WaitTime_Min2,
                                        fill = factor(Event_Time_HrGroup)
                                       )
                                   ) + 
  geom_boxplot(outlier.colour="red", notch=TRUE, na.rm = TRUE) +
  geom_text(data = CountValues_AllBus_RteHG,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 2.5,
            vjust = -0.5
           ) +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  coord_cartesian(# xlim = c(0, 180),
                  ylim = c(0, max(CountValues_AllBus_RteHG$VC))
                 ) +
  labs(title = "Waiting Time at a Given Stop",
       subtitle = ("Route X2"),
       x = "Hour Group",
       y = "Waiting Time (min)"
      ) 
# +
#   facet_wrap(~Stop_Zip
#              # nrow = 5
#             )

WaitTime_AllBus_RteHG_Box

Violin plots for WaitTime (Zip Code, by HourGroupZip).


WaitTime_AllBus_RteHG_Vln <- ggplot(Rte,
                                    aes(factor(Event_Time_HrGroup),
                                        WaitTime_Min2,
                                        fill = factor(Event_Time_HrGroup)
                                       )
                                   ) + 
  geom_violin(draw_quantiles = c(0.25, 0.5, 0.75),
              trim = TRUE,
              scale = "count",
              na.rm = TRUE,
              show.legend = NA,
              inherit.aes = TRUE
             ) +
  geom_text(data = CountValues_AllBus_RteHG,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 2.5,
            vjust = -0.5
           ) +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  coord_cartesian(# xlim = c(0, 180),
                  ylim = c(0, 45)
                 ) +
  labs(title = "Waiting Time at a Given Stop",
       subtitle = ("(Route X2)"),
       x = "Hour Group",
       y = "Waiting Time (min)"
      ) +
  facet_wrap(~Stop_Zip
             # nrow = 5
            )

WaitTime_AllBus_RteHG_Vln

X2 Percentiles Line Graph Test.


X2_Pct <- select(WaitTime_RteCnts,
                 Route,
                 Stop_Zip,
                 Event_Time_Date,
                 Event_Time_Day,
                 Event_Time_HrGroup,
                 Event_Time_Hr,
                 Latitude,
                 Longitude,
                 WaitTime_Min2
                ) %>% 
  filter(Route == "X2") %>% 
  group_by(Event_Time_Hr,
           Stop_Zip
          ) %>% 
  summarise(Pct50 = quantile(WaitTime_Min2, probs = 0.5, na.rm = TRUE),
            Pct60 = quantile(WaitTime_Min2, probs = 0.6, na.rm = TRUE),
            Pct70 = quantile(WaitTime_Min2, probs = 0.7, na.rm = TRUE),
            Pct80 = quantile(WaitTime_Min2, probs = 0.8, na.rm = TRUE),
            Pct90 = quantile(WaitTime_Min2, probs = 0.9, na.rm = TRUE)
           )

str(X2_Pct)
View(X2_Pct)


X2_Long <- gather(X2_Pct,
                  key = Percentile,
                  value = Pctile,
                  Pct50,
                  Pct60,
                  Pct70,
                  Pct80,
                  Pct90
                )

str(X2_Long)
View(X2_Long)


X2_WaitByHr_Line <- ggplot(X2_Long,
                           aes(x = Event_Time_Hr,
                               y = Pctile,
                               factor(Percentile),
                               color = Percentile
                              )
                          ) +
  geom_line() +
  theme(legend.title=element_blank(),
        legend.position = "bottom"
       ) +
  coord_cartesian(xlim = c(0, 23)
                  # ylim = c(0, 45)
                 ) + 
  scale_x_continuous(breaks = seq(0, 23, 2)
                    ) +
  labs(title = "Waiting Time Throughout the Day",
       subtitle = ("(Route X2)"),
       x = "Hour of the Day",
       y = "Waiting Time (min)"
      ) +
  facet_wrap(~Stop_Zip)

X2_WaitByHr_Line

GET DATA READY FOR SHINY – GET DATA READY FOR SHINY – GET DATA READY FOR SHINY GET DATA READY FOR SHINY – GET DATA READY FOR SHINY – GET DATA READY FOR SHINY GET DATA READY FOR SHINY – GET DATA READY FOR SHINY – GET DATA READY FOR SHINY

BaseData: Used in plots by hour and zipcode (first two Shiny tabs).


# str(WaitTime_RteCnts)

Shiny_WaitData_Base <- select(WaitTime_RteCnts,
                              Route,
                              Stop_Zip,
                              Event_Time,
                              Event_Time_Date,
                              Event_Time_Day,
                              Event_Time_HrGroup,
                              Event_Time_Hr,
                              Latitude,
                              Longitude,
                              WaitTime_Min2
                             ) %>% 
  mutate(Event_Time_YrMthDayHr = floor_date(Event_Time, "hour")
        ) %>% 
  rename(ZipCode = Stop_Zip,
         HourGroup = Event_Time_HrGroup,
         Date = Event_Time_Date,
         Day = Event_Time_Day,
         Hour = Event_Time_Hr,
         WaitTime_Min = WaitTime_Min2
        ) %>% 
  filter(WaitTime_Min <= 180)

Shiny_WaitData_Base$Route <- factor(Shiny_WaitData_Base$Route)

str(Shiny_WaitData_Base)
View(tail(Shiny_WaitData_Base, 500))

saveRDS(Shiny_WaitData_Base,
        "Shiny_WaitData_Base.rds"
       )

Prep data for mapping.


# devtools::install_github("dkahle/ggmap")
# devtools::install_github("hadley/ggplot2")
# install.packages("ggmap", type = "source")

# devtools::install_github('hadley/ggplot2')
devtools::install_github("hadley/ggplot2@v2.2.0")
# devtools::install_github('thomasp85/ggforce')
# devtools::install_github('thomasp85/ggraph')
# devtools::install_github('slowkow/ggrepel')


tract <- 
  readOGR(dsn = "/Users/mdturse/Desktop/Analytics/DCMetroBus/tl_2016_us_zcta510",
          layer = "tl_2016_us_zcta510"
         )
  
class(tract)

# convert the GEOID to a character
tract@data$GEOID <- as.character(tract@data$GEOID)
str(tract@data)


ggtract <- tidy(tract, region = "GEOID")

# str(ggtract)
# summary(ggtract)
# View(head(ggtract, 50))



# str(Shiny_WaitData_Base)

ZipWaitTest <- filter(Shiny_WaitData_Base,
                      WaitTime_Min <= 180 &
                        !is.na(ZipCode)
                     ) %>% 
  group_by(ZipCode,
           Event_Time_YrMthDayHr
           # Event_Time_Day,
           # Event_Time_Hr
          ) %>% 
  summarise(Pct80 = quantile(WaitTime_Min, probs = 0.8, na.rm = TRUE)
           ) %>% 
  arrange(# Event_Time_Hr,
          ZipCode,
          Event_Time_YrMthDayHr
         ) %>% 
  as.data.frame() %>% 
  mutate(Event_Time_DateNew = floor_date(Event_Time_YrMthDayHr, "day"),
         Event_Time_HrNew = hour(Event_Time_YrMthDayHr),
         Pct80_Level = factor(ifelse(Pct80 < 10,
                                     "Below 10",
                              ifelse(Pct80 < 20,
                                     "Below 20",
                              ifelse(Pct80 < 30,
                                     "Below 30",
                              ifelse(Pct80 < 40,
                                     "Below 40",
                              ifelse(Pct80 < 50,
                                     "Below 50",
                              ifelse(Pct80 < 60,
                                     "Below 60",
                                     "60 Plus"
                                    )))))),
                              levels = c("Below 10", "Below 20", "Below 30", 
                                         "Below 40", "Below 50", "Below 60", "60 Plus"
                                        ),
                              ordered = TRUE
                             )
        )

str(ZipWaitTest)
ZipWaitTest$ZipCode <- as.character(ZipWaitTest$ZipCode)
str(ZipWaitTest)
summary(ZipWaitTest)

View(head(ZipWaitTest, 500))


StopZip_Left <- left_join(ZipWaitTest,
                          ggtract,
                          by = c("ZipCode" = "id")
                         )

str(StopZip_Left)
summary(StopZip_Left)

Test mapping functionaltiy.


map <- get_map(location = c(lon = -77.03676, lat = 38.89784),
               source = "google",
               # maptype = "roadmap"
               zoom = 12
              )

ggmap(map) +
  geom_polygon(aes(x = long, 
                   y = lat, 
                   group = group,
                   fill = Pct80_Level
                  ), 
               data = filter(StopZip_Left,
                             Event_Time_YrMthDayHr == as.POSIXct("2016-10-07 20:00:00")
                             # &
                             #   Stop_Zip == "20003"
                            ),
               colour = "gray1", 
               # fill = 'black', 
               alpha = .4, 
               size = .3
              ) +
# +
  # scale_fill_gradientn(colours = c("white", "royalblue4", "red"),
  #                      #  "lightsteelblue4",
  #                      # "lightpink1",
  #                      # values=cbPalette,
  #                      # values = c(1,0.5, .3, .2, .1, 0)
  #                      na.value = "black",
  #                      breaks = c(seq(0, 180, 30))
  #                      # values = rescale()
  #                     ) 
# +
  scale_fill_brewer(palette = "Spectral", # "YlOrRd" # "Set1",
                    direction = -1,
                    limits = levels(StopZip_Left$Pct80_Level)
                   )

Shiny data for mapping (used in 3rd tab).


View(head(filter(StopZip_Left,
                 Event_Time_HrNew == 15
                ),
          500
         )
    )

Shiny_WaitData_Map <- StopZip_Left %>% 
  rename(YrMthDayHr = Event_Time_YrMthDayHr,
         YrMthDay = Event_Time_DateNew,
         Hour = Event_Time_HrNew
        )

str(Shiny_WaitData_Map)


Shiny_WaitData_Map_Wed <- filter(Shiny_WaitData_Map,
                                 YrMthDay == as.POSIXct("2016-10-05")
                                )

str(Shiny_WaitData_Map_Wed)
summary(Shiny_WaitData_Map_Wed)


saveRDS(Shiny_WaitData_Map,
        "Shiny_WaitData_Map.rds"
       )

saveRDS(Shiny_WaitData_Map_Wed,
        "Shiny_WaitData_Map_Wed.rds"
       )

Clustering

Data prep.


rm(tract, ggtract, StopZip_Left, ZipWaitTest, Shiny_WaitData_Base, Shiny_WaitData_Map, Shiny_WaitData_Map_Wed)


dim(NewTravTime)
dim(WaitTime_RteCnts)


str(select(NewTravTime,
           -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
          )
   )
str(select(NewTravTime,
           matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
          )
   )
str(WaitTime_RteCnts)

 
 
# ClustData <- select(WaitTime_RteCnts,
#                     group,
#                     BusDay_EventNum,
#                     Route,
#                     RteChange2,
#                     RouteAlt,
#                     DirChange2,
#                     Route_Direction,
#                     Stop_Sequence,
#                     StopID_Indicator,
#                     Stop_County,
#                     Stop_City,
#                     Stop_Zip,
#                     Event_Time_Hr,
#                     Dwell_Time2,
#                     TravelDistance_Mi_NewHvrs,
#                     TravelDistance_Mi_NewHvrs_Label,
#                     TT_Sec_New,
#                     TT_Sec_New_Label,
#                     WaitTime_Min2
#                    ) %>% 
#   filter(WaitTime_Min2 <= 180) %>% 
#   mutate(SpeedAvg_Mph_TDMNH_TTSN = TravelDistance_Mi_NewHvrs / 
#            (TT_Sec_New / 60 / 60)
#         )
# %>% 
#   select_if(ClustData,
#             function(col) is.numeric(col) |
#               is.integer(col)
#            ) %>% 
#    scale()

# str(ClustData)
# View(tail(ClustData, 500))
# rownames(ClustData) <- ClustData$Route
# ClustData$Route <- as.factor(ClustData$Route)
# str(ClustData)
# head(ClustData)


RouteStats <- filter(WaitTime_RteCnts,
                     WaitTime_Min2 <= 180
                    ) %>% 
  mutate(SpeedAvg_Mph_TDMNH_TTSN = TravelDistance_Mi_NewHvrs / (TT_Sec_New / 60 / 60)
        ) %>% 
  group_by(Route) %>% 
  summarise(BusDayEventNum_Mean = mean(BusDay_EventNum, na.rm = TRUE),
            BusDayEventNum_Pct10 = quantile(BusDay_EventNum, probs = 0.10, na.rm = TRUE),
            BusDayEventNum_Pct25 = quantile(BusDay_EventNum, probs = 0.25, na.rm = TRUE),
            BusDayEventNum_Pct50 = quantile(BusDay_EventNum, probs = 0.50, na.rm = TRUE),
            BusDayEventNum_Pct75 = quantile(BusDay_EventNum, probs = 0.75, na.rm = TRUE),
            BusDayEventNum_Pct90 = quantile(BusDay_EventNum, probs = 0.90, na.rm = TRUE),
            StopSequence_Mean = mean(Stop_Sequence, na.rm = TRUE),
            StopSequence_Pct10 = quantile(Stop_Sequence, probs = 0.10, na.rm = TRUE),
            StopSequence_Pct25 = quantile(Stop_Sequence, probs = 0.25, na.rm = TRUE),
            StopSequence_Pct50 = quantile(Stop_Sequence, probs = 0.50, na.rm = TRUE),
            StopSequence_Pct75 = quantile(Stop_Sequence, probs = 0.75, na.rm = TRUE),
            StopSequence_Pct90 = quantile(Stop_Sequence, probs = 0.90, na.rm = TRUE),
            EventTimeHr_Mean = mean(Event_Time_Hr, na.rm = TRUE),
            EventTimeHr_Pct10 = quantile(Event_Time_Hr, probs = 0.10, na.rm = TRUE),
            EventTimeHr_Pct25 = quantile(Event_Time_Hr, probs = 0.25, na.rm = TRUE),
            EventTimeHr_Pct50 = quantile(Event_Time_Hr, probs = 0.50, na.rm = TRUE),
            EventTimeHr_Pct75 = quantile(Event_Time_Hr, probs = 0.75, na.rm = TRUE),
            EventTimeHr_Pct90 = quantile(Event_Time_Hr, probs = 0.90, na.rm = TRUE),
            DwellTime2_Mean = mean(Dwell_Time2, na.rm = TRUE),
            DwellTime2_Pct10 = quantile(Dwell_Time2, probs = 0.10, na.rm = TRUE),
            DwellTime2_Pct25 = quantile(Dwell_Time2, probs = 0.25, na.rm = TRUE),
            DwellTime2_Pct50 = quantile(Dwell_Time2, probs = 0.50, na.rm = TRUE),
            DwellTime2_Pct75 = quantile(Dwell_Time2, probs = 0.75, na.rm = TRUE),
            DwellTime2_Pct90 = quantile(Dwell_Time2, probs = 0.90, na.rm = TRUE),
            TravDistMi_Mean = mean(TravelDistance_Mi_NewHvrs, na.rm = TRUE),
            TravDistMi_Pct10 = quantile(TravelDistance_Mi_NewHvrs,
                                        probs = 0.10, na.rm = TRUE
                                       ),
            TravDistMi_Pct25 = quantile(TravelDistance_Mi_NewHvrs,
                                        probs = 0.25, na.rm = TRUE
                                       ),
            TravDistMi_Pct50 = quantile(TravelDistance_Mi_NewHvrs,
                                        probs = 0.50, na.rm = TRUE
                                       ),
            TravDistMi_Pct75 = quantile(TravelDistance_Mi_NewHvrs,
                                        probs = 0.75, na.rm = TRUE
                                       ),
            TravDistMi_Pct90 = quantile(TravelDistance_Mi_NewHvrs,
                                        probs = 0.90, na.rm = TRUE
                                       ),
            TravTimSec_Mean = mean(TT_Sec_New, na.rm = TRUE),
            TravTimSec_Pct10 = quantile(TT_Sec_New, probs = 0.10, na.rm = TRUE),
            TravTimSec_Pct25 = quantile(TT_Sec_New, probs = 0.25, na.rm = TRUE),
            TravTimSec_Pct50 = quantile(TT_Sec_New, probs = 0.50, na.rm = TRUE),
            TravTimSec_Pct75 = quantile(TT_Sec_New, probs = 0.75, na.rm = TRUE),
            TravTimSec_Pct90 = quantile(TT_Sec_New, probs = 0.90, na.rm = TRUE),
            WaitTimMin_Mean = mean(WaitTime_Min2, na.rm = TRUE),
            WaitTimMin_Pct10 = quantile(WaitTime_Min2, probs = 0.10, na.rm = TRUE),
            WaitTimMin_Pct25 = quantile(WaitTime_Min2, probs = 0.25, na.rm = TRUE),
            WaitTimMin_Pct50 = quantile(WaitTime_Min2, probs = 0.50, na.rm = TRUE),
            WaitTimMin_Pct75 = quantile(WaitTime_Min2, probs = 0.75, na.rm = TRUE),
            WaitTimMin_Pct90 = quantile(WaitTime_Min2, probs = 0.90, na.rm = TRUE)
           ) %>% 
  as.data.frame()

str(RouteStats)

rownames(RouteStats) <- RouteStats$Route
str(RouteStats)
View(RouteStats)


RouteStats_Scaled <- select(RouteStats,
                            -Route
                           ) %>% 
  scale()

str(RouteStats_Scaled)
class(RouteStats_Scaled)
View(RouteStats_Scaled)

summary(RouteStats)
summary(RouteStats_Scaled)

# <- select_if(ClustData,
#                               function(col) is.numeric(col) |
#                                 is.integer(col)
#                              ) %>% 
  # scale() %>% 
  # as.data.frame() %>% 
  # na.omit()

# str(ClustData_NoFact)
# summary(ClustData_NoFact)

PCA


Trnsfrm <- preProcess(select(RouteStats,
                             -Route
                            ),
                      method = c("BoxCox", "center", "scale", "pca")
                     )

# loadings
Trnsfrm$rotation

RouteStats_Pca <- predict(Trnsfrm, RouteStats) %>% 
  select(-Route)
RouteStats_Pca

Clustering.

Are the data clusterable?


##### Are the data clusterable?
# gradient_col <- list(low = "steelblue", high = "white")
ClustData_Ends <- get_clust_tendency(RouteStats_Pca,
                                     n = nrow(RouteStats_Pca
                                             ) - 1,
                                     # gradient = gradient_col,
                                     seed = 123456789
                                    )

str(ClustData_Ends)

# Hopkins statistic
ClustData_Ends$hopkins_stat  # value of 0.1657494 implies that the data are not uniformly distributed (they are "clusterable")

#plot
ClustData_Ends$plot

Clustering. How many clusters are there?

kmeans, pam, and hierarchical clustring methods, using within sum of squares and silhouette measures.


# class(RouteStats_Pca)

fviz_nbclust(RouteStats_Pca, kmeans, method = "wss")  # ~8 clusters
fviz_nbclust(RouteStats_Pca, pam, method = "wss")  # ~6 clusters
fviz_nbclust(RouteStats_Pca, hcut, method = "wss")  # ~6 clusters

fviz_nbclust(RouteStats_Pca, kmeans, method = "silhouette")  # 2 clusters
fviz_nbclust(RouteStats_Pca, pam, method = "silhouette")  # 2 clusters
fviz_nbclust(RouteStats_Pca, hcut, method = "silhouette",
             hc_method = "complete")  # 2 clusters

Clustering. How many clusters are there?

kmeans method with the gap statistic, using bootstrap.


# Compute gap statistic
# kmeans version
set.seed(123456789)
# system.time(
gap_stat_km <- clusGap(RouteStats_Pca,
                       FUN = kmeans,
                       nstart = 25,
                       K.max = 10,
                       B = 500
                      )
# )

# Print
print(gap_stat_km, method = "Tibs2001SEmax")
print(gap_stat_km)


# pam version
set.seed(123456789)
gap_stat_pm <- clusGap(RouteStats_Pca,
                       FUN = pam,
                       K.max = 10,
                       B = 500
                      )

# Print
print(gap_stat_pm, method = "Tibs2001SEmax")
print(gap_stat_pm)


# hierarchical version
set.seed(123456789)
gap_stat_hcut <- clusGap(RouteStats_Pca,
                         FUN = hcut,
                         K.max = 10,
                         B = 500
                        )

# Print
print(gap_stat_hcut, method = "Tibs2001SEmax")
print(gap_stat_hcut)



# Plot kmeans
fviz_gap_stat(gap_stat_km, 
              maxSE = list(method = "Tibs2001SEmax")
             )  # 1 cluster

# Plot pam
fviz_gap_stat(gap_stat_pm, 
              maxSE = list(method = "Tibs2001SEmax")
             )  # 2 cluster

# Plot hierarchical
fviz_gap_stat(gap_stat_hcut, 
              maxSE = list(method = "Tibs2001SEmax")
             )  # 1 cluster

Clustering. How many clusters are there?

kmeans method with various different statistics.


# str(iris)

nb <- NbClust(RouteStats_Pca, #scale(iris[ ,-5]),
              distance = "euclidean",
              min.nc = 2,
              max.nc = 15,
              method = "kmeans",
              index = "all"
             )

fviz_nbclust(nb) + theme_minimal()

Clustering. How many clusters are there?

Hierarchical clustering method. Particularly looking at silhouette statistics.

# Visualize
HCDend_K2
HCDend_K3

HCDend_K4

HCDend_K5

HCDend_K6

HCDend_K7

HCDend_K8

HCDend_K9

HCDend_K10

HCDend_K11

HCDend_K12

HCDend_K13

HCDend_K14

HCDend_K15

HCSil_K2

HCSil_K3

HCSil_K4

HCSil_K5

HCSil_K6

HCSil_K7

HCSil_K8

HCSil_K9

HCSil_K10

HCSil_K11

HCSil_K12

HCSil_K13

HCSil_K14

HCSil_K15

HCSilWidth_AllK

Using kmeans, PAM, and Hierarchical clustering methods, we can say we probably have aroun 2 clusters.

Let’s try density clustering. (This tends to show that maybe there is only one “cluster,” meaning that data are not clusterable.)

# Compute DBSCAN using fpc package
kNNdistplot(RouteStats_Pca, k = 10)
abline(h = 8.5, lty = 2)
set.seed(123456789)
db <- fpc::dbscan(RouteStats_Pca,
                  eps = 8.5,
                  MinPts = 10
                )
str(db)
List of 4
 $ cluster: num [1:268] 1 1 1 1 1 1 1 1 1 1 ...
 $ eps    : num 8.5
 $ MinPts : num 10
 $ isseed : logi [1:268] TRUE TRUE TRUE TRUE TRUE TRUE ...
 - attr(*, "class")= chr "dbscan"
db
dbscan Pts=268 MinPts=10 eps=8.5
       0   1
border 5   7
seed   0 256
total  5 263
# Plot DBSCAN results
fviz_cluster(db,
             RouteStats_Pca,
             stand = FALSE,
             frame = FALSE,
             geom = "point"
            )
argument frame is deprecated; please use ellipse instead.

Investigating TravelTime_Sec.


View(filter(TTLargeRteChng,
            !is.na(TravelTime_Sec) &
              RteChange2 == "Same"
           ) %>% 
       arrange(desc(TravelTime_Sec),
               SpeedAvg_Mph_NewHvrs
              ) %>%
       head(500)
    )


# examples where TravelTime_Sec is small (1 sec) and SpeedAvg_Mph_NewHvrs is large.
View(select(NewTravTime,
            # -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
            -(TD_Mi_q2:TD_Mi_SSHG_Cnt_F),
            -(TT_Hr_q2:TT_Hr_SSHG_Cnt_F)
           ) %>% 
       filter((RowNum_OG >= 2217353 & RowNum_OG <= 2217373) | # 2217363
                (RowNum_OG >= 3090321 & RowNum_OG <= 3090341) | # 3090331
                (RowNum_OG >= 80764 & RowNum_OG <= 80784) | # 80774
                (RowNum_OG >= 33840 & RowNum_OG <= 33860) # 33850
           )
    )






# examples where TravelTime_Sec is large and SpeedAvg_Mph_NewHvrs is small.
View(filter(TTLargeRteChng,
            (RowNum_OG >= 2250290 & RowNum_OG <= 2250310) | # 2250300
              (RowNum_OG >= 867717 & RowNum_OG <= 867737) | # 867727
              (RowNum_OG >= 864379 & RowNum_OG <= 864399) | # 864389
              (RowNum_OG >= 808395 & RowNum_OG <= 808415) # 808405
           )
    )

         
         
# examples where TravelTime_Sec is unusually small (with TravelDistance_Mi values that are large).
View(filter(AllDays_NewTravelDist,
            (RowNum_OG >= 1042228 & RowNum_OG <= 1042248) | # 1042238
                (RowNum_OG >= 53816 & RowNum_OG <= 53836) | # 53826
                (RowNum_OG >= 360571 & RowNum_OG <= 360591) | # 360581
                (RowNum_OG >= 502271 & RowNum_OG <= 502291) # 502281 (can't explian the weird TravelTime_Sec calculation here - it's not even an integer!)
           )
    )

# still trying to explain 502281...on the day of this weirdness, the bus was only in circulation for 4-5 stops (~20 minutes) on that day (Oct 6)
View(filter(AllDays_NewTravelDist,
            Bus_ID == 2711
           )
    )


# exploring large values for TravelTime_Sec
View(filter(AllDays_NewTravelDist,
            TravelTime_Sec == 300
           ) %>% 
       arrange(desc(TravelTime_Sec),
               SpeedAvg_Mph2
              )
    )

# examples where TravelTime_Sec is unusually large (with TravelDistance_Mi values that are small, so SpeedAvg_Mph values are very small).
View(filter(AllDays_NewTravelDist,
            (RowNum_OG >= 2627459 & RowNum_OG <= 2627479) | # 2627469
                (RowNum_OG >= 2193344 & RowNum_OG <= 2193364) | # 2193354
                (RowNum_OG >= 1644123 & RowNum_OG <= 1644143) | # 1644133
                (RowNum_OG >= 869600 & RowNum_OG <= 869620) # 869610
           )
    )

Investigation of SpeedAvg_Mph2

View(Speed_Pctiles): 90% of SpeedAvg_Mph2 are between ~3mph and ~66mph.


Speed_Ntile <- as.data.frame(AllDays_NewTravelDist$SpeedAvg_Mph2) %>% 
  mutate(Pctile = ntile(AllDays_NewTravelDist$SpeedAvg_Mph2, 100),
         MinR = min_rank(AllDays_NewTravelDist$SpeedAvg_Mph2),
         PctR = percent_rank(AllDays_NewTravelDist$SpeedAvg_Mph2),
         PctR_Round = round(PctR, 2)
        ) 

colnames(Speed_Ntile)[1] <- "SpeedAvg_Mph2"
str(Speed_Ntile)

Speed_Ntile_Rows <- nrow(Speed_Ntile)

View(tail(Speed_Ntile, 500))


Speed_Pctiles <- group_by(Speed_Ntile,
                          PctR_Round
                         ) %>% 
  summarise(
    MinSpeedAtPctile = min(SpeedAvg_Mph2),
    CntsAtPctile = n(),
    PctsAtPctile = CntsAtPctile / Speed_Ntile_Rows
  ) %>% 
  mutate(CumSumPAtP = cumsum(PctsAtPctile)
        )

View(Speed_Pctiles)

Investigation of SpeedAvg_Mph2.

Exploring the removal of outlier TravelTime_Sec and TravelDistance_Mi.


summary(select(AllDays_NewTravelDist,
               SpeedAvg_Mph,
               SpeedAvg_Mph2
              )
       )

summary(select(filter(AllDays_NewTravelDist,
                      TravelDistance_Mi > 0.0001893939 & # lowest non-zero percentile
                        TravelDistance_Mi < 1.0812500000 & # 99th percentile
                        TravelTime_Sec > 10.050000 & # 2nd percentile
                        TravelTime_Sec < 293.000000 # 98th percentile
                     ),
               SpeedAvg_Mph,
               SpeedAvg_Mph2
              )
       )

Investigation of SpeedAvg_Mph2.

Histogram of SpeedAvg_Mph2.


Speed_HistDen <- ggplot(filter(AllDays_NewTravelDist,
                               !is.na(SpeedAvg_Mph2)
                              ),
                        aes(x = SpeedAvg_Mph2,
                            y = ..density..
                           )
                       ) +
  geom_histogram(binwidth = 5, fill = "lightblue", colour = "grey60", size = 0.2) +
  geom_line(stat = "density", colour = "red") +
  stat_bin(binwidth = 5,
           geom = "text",
           size = 2.5,
           vjust = 1.5,
           aes(label = format(..count.., big.mark = ",")
              ),
          ) +
  # geom_text(aes(label = format(..count.., big.mark = ",")
  #              ),
  #           size = 3,
  #           nudge_y = (..count.. * 0.1)
  #          ) +
  coord_cartesian(xlim = c(0, 70), ylim = c(0, 0.04)
                 ) +
  #  theme(legend.position="none") +
  labs(title = "Variation in Travel Speed",
       x = "Average Speed (mph)",
       y = "Density"
      )

Speed_HistDen

Investigation of SpeedAvg_Mph2.

Histogram of SpeedAvg_Mph2 after removing outlier TravelTime_Sec and TravelDistance_Mi.


View(TravDistMiNew_Pctiles)
View(TravTimeHr_Pctiles)

SpeedNoOutlier_HistDen <- ggplot(filter(AllDays_NewTravelDist,
                                        !is.na(SpeedAvg_Mph2) &
                                          TravelDistance_Mi_New > 0.077841005 & # 5th percentile
                                          # TravelDistance_Mi_New < 1.0812500000 & # 99th percentile
                                          TravelTime_Sec > 12.100000 # 4th percentile
                                          # TravelTime_Sec < 293.000000 # 98th percentile
                                       ),
                                 aes(x = SpeedAvg_Mph2,
                                     y = ..density..
                                    )
                                ) +
  geom_histogram(binwidth = 5, fill = "lightblue", colour = "grey60", size = 0.2) +
  geom_line(stat = "density", colour = "red") +
  stat_bin(binwidth = 5,
           geom = "text",
           size = 2.5,
           vjust = 1.5,
           aes(label = format(..count.., big.mark = ",")
              ),
          ) +
  # geom_text(aes(label = format(..count.., big.mark = ",")
  #              ),
  #           size = 3,
  #           nudge_y = (..count.. * 0.1)
  #          ) +
  coord_cartesian(xlim = c(0, 70), ylim = c(0, 0.04)
                 ) +
  #  theme(legend.position="none") +
  labs(title = "Variation in Travel Speed",
       subtitle = "(removed low outliers of Travel Distance and Travel Time)",
       x = "Average Speed (mph)",
       y = "Density"
      )

SpeedNoOutlier_HistDen

Investigation of SpeedAvg_Mph2.

New dataset (NoOutliers_TravelDistNTime) when removing outlier low values of TravelDistance_Mi_New and TravelTime_Sec.


View(TravDistMiNew_Pctiles)
View(TravTimeHr_Pctiles)

NoOutliers_TravelDistNTime <- filter(AllDays_NewTravelDist,
                                     TravelDistance_Mi_New > .077841005 & # 5th percentile
                                       # TravelDistance_Mi_New < 1.0812500000 & # 99th percentile
                                       TravelTime_Sec > 12.100000 # 4th percentile
                                       # TravelTime_Sec < 293.000000 # 98th percentile
                                    )

nrow(AllDays_NewTravelDist) - nrow(NoOutliers_TravelDistNTime)

str(NoOutliers_TravelDistNTime)
summary(NoOutliers_TravelDistNTime)

Investigation of SppedAvg_Mph2.

View(Speed_NoOut_Pctiles): Aproximately 90% of SpeedAvg_Mph2 values are between ~4mph and ~56mph.


Speed_NoOut_Ntile <- as.data.frame(NoOutliers_TravelDistNTime$SpeedAvg_Mph2) %>% 
  mutate(Pctile = ntile(NoOutliers_TravelDistNTime$SpeedAvg_Mph2, 100),
         MinR = min_rank(NoOutliers_TravelDistNTime$SpeedAvg_Mph2),
         PctR = percent_rank(NoOutliers_TravelDistNTime$SpeedAvg_Mph2),
         PctR_Round = round(PctR, 2)
        ) 

colnames(Speed_NoOut_Ntile)[1] <- "SpeedAvg_Mph2"
str(Speed_NoOut_Ntile)

Speed_NoOut_Ntile_Rows <- nrow(Speed_NoOut_Ntile)

View(tail(Speed_NoOut_Ntile, 500))


Speed_NoOut_Pctiles <- group_by(Speed_NoOut_Ntile,
                                PctR_Round
                               ) %>% 
  summarise(
    MinSpeedAtPctile = min(SpeedAvg_Mph2),
    CntsAtPctile = n(),
    PctsAtPctile = CntsAtPctile / Speed_NoOut_Ntile_Rows
  ) %>% 
  mutate(CumSumPAtP = cumsum(PctsAtPctile)
        )

View(Speed_NoOut_Pctiles)

Investigation of SppedAvg_Mph2.

Exloring odd/impossible values.


# Exploring when SpeedAvg_Mph2 is NA  --  does not occur at all
nrow(filter(NoOutliers_TravelDistNTime,
            is.na(SpeedAvg_Mph2)
           )
    )


# Exploring when SpeedAvg_Mph2 is zero  --  does not occur at all
nrow(filter(NoOutliers_TravelDistNTime,
            SpeedAvg_Mph2 == 0
           )
    )


# examples where SpeedAvg_Mph2 < 3.2848770
View(filter(AllDays_NewTravelDist,
            SpeedAvg_Mph2 > 0 &
              SpeedAvg_Mph2 < 3.2848770
           ) %>% 
       arrange(SpeedAvg_Mph2)
    )

# examples where SpeedAvg_Mph2 < 3.2848770
View(filter(AllDays_NewTravelDist,
            (RowNum_OG >= 485338 & RowNum_OG <= 485358) | # 485348  --  Extreme travel time, Route Change
                (RowNum_OG >= 346952 & RowNum_OG <= 346972) | # 346962  -- Extreme travel time, Route Change 
                (RowNum_OG >= 70494 & RowNum_OG <= 70514) | # 70504  --  Extreme travel time, Route Change
                (RowNum_OG >= 2051846 & RowNum_OG <= 2051866) # 2051856  --  Extreme travel time, Route Change
           )
    )

Investigation of SpeedAvg_Mph2.

Limit the dataset based on SpeedAvg_Mph2.


NoOutliersSpeed <- filter(NoOutliers_TravelDistNTime,
                          between(SpeedAvg_Mph2,
                                  4.069300, # 5th percentile
                                  56.05651 #95th percentile
                                 )
                          )

nrow(NoOutliers_TravelDistNTime) - nrow(NoOutliersSpeed)

summary(NoOutliersSpeed)

TravelTime now looks like it has some odd values on the high end. So let’s look at those.

View(TravTime_NoOut_Pctiles): Virtually all trips should take less than 5 minutes. (The 99th percentile of of TravelTime is approximately 8 minutes.)


TravTime_NoOut_Ntile <- as.data.frame(NoOutliersSpeed$TravelTime_Hr) %>% 
  mutate(Pctile = ntile(NoOutliersSpeed$TravelTime_Hr, 100),
         MinR = min_rank(NoOutliersSpeed$TravelTime_Hr),
         PctR = percent_rank(NoOutliersSpeed$TravelTime_Hr),
         PctR_Round = round(PctR, 2)
        )

colnames(TravTime_NoOut_Ntile)[1] <- "TravelTime_Hr"
str(TravTime_NoOut_Ntile)

TravTime_NoOut_Ntile_Rows <- nrow(TravTime_NoOut_Ntile)

View(tail(TravTime_NoOut_Ntile, 500))


TravTime_NoOut_Pctiles <- group_by(TravTime_NoOut_Ntile,
                                   PctR_Round
                                  ) %>% 
  summarise(
    MinTravTimeHrAtPctile = min(TravelTime_Hr),
    CntsAtPctile = n(),
    PctsAtPctile = CntsAtPctile / TravTime_NoOut_Ntile_Rows
  ) %>% 
  mutate(CumSumPAtP = cumsum(PctsAtPctile),
         MinTravTimeSecAtPctile = MinTravTimeHrAtPctile * (60 * 60)
        )

View(TravTime_NoOut_Pctiles)

Investigating odd TravelTime_Sec values.

Trips longer than ~8 minutes.


View(filter(NoOutliersSpeed,
            TravelTime_Sec > 491 # min at the 100th percentile
           ) %>% 
       arrange(desc(TravelTime_Sec)
              )
    )

# examples of TravelTime_Sec values that are largest.
View(filter(NoOutliersSpeed,
            (RowNum_OG >= 2071759 & RowNum_OG <= 2071779) | # 2071769  --  results from a route change, and a 3hr+ wait before the new route starts
                (RowNum_OG >= 1473686 & RowNum_OG <= 1473706) | # 1473696  --  results from a route change, and a 3hr wait before the new route starts
                (RowNum_OG >= 1222822 & RowNum_OG <= 1222842) | # 1222832  --  results from a route change, and a 3hr wait before the new route starts
                (RowNum_OG >= 3046089 & RowNum_OG <= 3046109) # 3046099  --  results from a route change, and a 3hr wait before the new route starts
           )
    )


# examples of TravelTime_Sec values that are the smallest of the large.
View(filter(NoOutliersSpeed,
            (RowNum_OG >= 3044689 & RowNum_OG <= 3044709) | # 3044699  --  results from a route change
                (RowNum_OG >= 3022358 & RowNum_OG <= 3022378) | # 3022368  --  results from a route change
                (RowNum_OG >= 2993016 & RowNum_OG <= 2993036) | # 2993026  --  results from a previous route change (change occurred in deleted row)
                (RowNum_OG >= 2683703 & RowNum_OG <= 2683723) # 2683713  --  results from a previous route change (change occurred in deleted row)
           )
    )

Let’s look at the TravelTime_Sec values and route changes (DirChange2).

The 99th percentile of TravelTime_Sec for both, all trips, and just those trips NOT involving route changes (DirChange2 = “Same”), is approximately 5min (300 sec).

Nota Bene: The percentile calculation here is defined slightly different than in most of the above analyses (which get the lowest value in the bin created by 100 ntiles).


summary(select(NoOutliersSpeed,
               TravelTime_Sec
              )
       )

summary(select(filter(NoOutliersSpeed,
                      DirChange2 == "Same"
                     ),
               TravelTime_Sec
              )
       )

summary(select(filter(NoOutliersSpeed,
                      DirChange2 == "Change"
                     ),
               TravelTime_Sec
              )
       )


TravTimeSec_Qtiles_df <- data.frame(PctValue = seq(0, 100, 1),
                                    All = seq(1, 101, 1),
                                    Same = seq(1, 101, 1),
                                    Change = seq(1, 101, 1)
                                   )

TravTimeSec_Qtiles_df[ , 2] <- quantile(select(NoOutliersSpeed,
                                               TravelTime_Sec
                                              ),
                                        probs = seq(0, 1, 0.01),
                                        na.rm = TRUE
                                       )

TravTimeSec_Qtiles_df[ , 3] <- quantile(select(filter(NoOutliersSpeed,
                                                      DirChange2 == "Same"
                                                     ),
                                               TravelTime_Sec
                                              ),
                                        probs = seq(0, 1, 0.01),
                                        na.rm = TRUE
                                       )

TravTimeSec_Qtiles_df[ , 4] <- quantile(select(filter(NoOutliersSpeed,
                                                      DirChange2 == "Change"
                                                     ),
                                               TravelTime_Sec
                                              ),
                                        probs = seq(0, 1, 0.01),
                                        na.rm = TRUE
                                       )

View(TravTimeSec_Qtiles_df)

Limit the dataset now based on TravelTime_Sec.


UpperLimitTravTime <- filter(NoOutliersSpeed,
                             TravelTime_Sec <= 491 # min at the 100th percentile
                             )

nrow(NoOutliersSpeed) - nrow(UpperLimitTravTime)

str(UpperLimitTravTime)

summary(UpperLimitTravTime)

Investigation of Dwell_Time2 (how long the bus is at a stop).

Differences between Dwell_Time (by WMATA) and Dwell_Time2 (by me) appear to be due to switches in RouteAlt. WMATA calculates Dwell_Time by an unknown process. The WMATA calculation is equal to my calculation, except for the records immedaitely before and after a RouteAlt switch (DirChange2).


View(filter(AllDays_NewOrder,
            Dwell_Time != Dwell_Time2
           )
    )


# Examples where the Dwell_Time and Dwell_Time2 are different
View(filter(AllDays_NewOrder,
            ( (RowNum_OG >= 65 & RowNum_OG <= 85) | # 75
                (RowNum_OG >= 162 & RowNum_OG <= 192) | # 172
                (RowNum_OG >= 431952 & RowNum_OG <= 431972) | # 431962
                (RowNum_OG >= 434595 & RowNum_OG <= 434615) # 434605  --  this record is NOT a route switch, but does has a Sequence switch (Me: should there really be a route switch here?)
            )
           )
    )

Investigation of Dwell_Time2 (how long the bus is at a stop).

First, create some “rank” stats. View(DT2_Pctiles): 95% of Dwell_Time2s are <= 23 seconds…but some weird (e.g., nearly 2 hour Dwell_Time2s exist).


DwellTime2_Ntile <- as.data.frame(AllDays_NewOrder$Dwell_Time2) %>% 
  mutate(Pctile = ntile(AllDays_NewOrder$Dwell_Time2, 100),
         MinR = min_rank(AllDays_NewOrder$Dwell_Time2),
         PctR = percent_rank(AllDays_NewOrder$Dwell_Time2),
         PctR_Round = round(PctR, 2)
        ) 

colnames(DwellTime2_Ntile)[1] <- "Dwell_Time2"
str(DwellTime2_Ntile)

DwellTime2_Ntile_Rows <- nrow(DwellTime2_Ntile)

View(tail(DwellTime2_Ntile, 500))


DwellTime2_Pctiles <- group_by(DwellTime2_Ntile,
                               PctR_Round
                              ) %>% 
  summarise(
    MinDwellAtPctile = min(Dwell_Time2),
    CntsAtPctile = n(),
    PctsAtPctile = CntsAtPctile / DwellTime2_Ntile_Rows
  ) %>% 
  mutate(CumSumPAtP = cumsum(PctsAtPctile)
        )

View(DwellTime2_Pctiles)

Investigation of Dwell_Time2 (how long the bus is at a stop).

Histogram of Dwell_Time2.


DwellTime2_HistDen <- ggplot(AllDays_NewOrder, aes(x = Dwell_Time2, y = ..density..)) +
  geom_histogram(binwidth = 1, fill = "lightblue", colour = "grey60", size = 0.2) +
  geom_line(stat = "density", colour = "red") +
  coord_cartesian(xlim = c(1, 25), ylim = c(0, 0.05)
                 ) +
  xlab("Time a Bus Stays at a Stop (sec)") + 
  ylab("Density") + 
  #  theme(legend.position="none") + 
  ggtitle(expression(atop("Variation in How Long a Bus Stays at a Stop"
                          # ,atop(italic("xxxxx"),"")
                         )
                    )
         )

DwellTime2_HistDen

Investigation of Dwell_Time2 (how long the bus is at a stop).

Looking at some weirdly long Dwell_Time2 values.


View(arrange(AllDays_NewOrder,
             desc(Dwell_Time2)
            )
    )


# examples of extremely large Dwell_Time2s
View(filter(AllDays_NewOrder,
            (RowNum_OG >= 292669 & RowNum_OG <= 292689) | # 292679
                (RowNum_OG >= 531057 & RowNum_OG <= 531077) | # 531067
                (RowNum_OG >= 1388627 & RowNum_OG <= 1388647) | # 1388637
                (RowNum_OG >= 1645711 & RowNum_OG <= 1645731) # 1645721
           )
    )


View(filter(AllDays_NewOrder,
            Dwell_Time2 == 0
           )
    )

Investigation of Delta_Time (how early or late the bus is).

View(DT2_Pctiles): 94% of Delta_Time values are between -236 seconds and 1,259 seconds. Roughly 66% of records are within 5 min late and 5 min early…but some weird (e.g., almost 50 minute late or 40 minute early) Delta_Times exist.

Note that Delta_Time is the difference from the scheduled bus arrival. So if two buses are scheduled to arrive at a destination at 10:00pm and 10:20pm, and if the 10:20pm bus has a Delta_Time of 5 minutes, there are 25 minutes between bus arrivals at the stop.

Also note that based on a comment at https://planitmetro.com/2016/11/16/data-download-metrobus-vehicle-location-data/, the Delta_Time values don’t appear to coincide with published bus schedules (e.g., the X2 departing every 8 minutes during peak hours).


DeltTime_Ntile <- as.data.frame(AllDays_NewOrder$Delta_Time) %>% 
  mutate(Pctile = ntile(AllDays_NewOrder$Delta_Time, 100),
         MinR = min_rank(AllDays_NewOrder$Delta_Time),
         PctR = percent_rank(AllDays_NewOrder$Delta_Time),
         PctR_Round = round(PctR, 2)
        ) 

colnames(DeltTime_Ntile)[1] <- "Delta_Time"
str(DeltTime_Ntile)

DeltTime_Ntile_Rows <- nrow(DeltTime_Ntile)

View(tail(DeltTime_Ntile, 500))


DeltTime_Pctiles <- group_by(DeltTime_Ntile,
                             PctR_Round
                            ) %>% 
  summarise(
    MinDeltTimeAtPctile = min(Delta_Time),
    CntsAtPctile = n(),
    PctsAtPctile = CntsAtPctile / DeltTime_Ntile_Rows
  ) %>% 
  mutate(CumSumPAtP = cumsum(PctsAtPctile)
        )

View(DeltTime_Pctiles)
DeltTime_Pctiles

# ~66% of rows are between 5 min late and 5 min early
nrow(filter(AllDays_NewOrder,
            Delta_Time >= -300 &
              Delta_Time <= 300
           )
    ) / nrow(AllDays_NewOrder)


# examples of weird large Delta_Times
View(filter(AllDays_NewOrder,
            Delta_Time < -4202 |
              Delta_Time > 1705
           ) %>% 
       arrange(desc(Delta_Time)
              )
    )

Investigation of Delta_Time (how early or late the bus is).

Delta_Time histogram.


DeltTime_HistDen <- ggplot(AllDays_NewOrder, aes(x = (Delta_Time / 60),
                                                 y = ..density..
                                                )
                          ) +
  geom_histogram(binwidth = (5/60), fill = "lightblue", colour = "grey60", size = 0.2) +
  geom_line(stat = "density", colour = "red") +
  coord_cartesian(xlim = c(-5, 5)) +
  xlab("Bus Lateness (min)") + 
  ylab("Density") + 
  #  theme(legend.position="none") + 
  ggtitle(expression(atop("Variation in How Early/Late a Bus Is",
                          atop(italic("(positive values are late arrivals)"),
                               ""
                              )
                         )
                    )
         )

DeltTime_HistDen

Investigation of Delta_Time (how early or late the bus is).

Delta_Time boxplot.


# Count_Values is needed to display the medians on the box plots
Count_Values <- ddply(AllDays_NewOrder,
                      .(Event_Time_HrGroup),
                      summarise,
                      Value_Counts = median(Delta_Time / 60, na.rm = TRUE)
                     )

DeltTime_BoxPlot <- ggplot(AllDays_NewOrder,
                           aes(factor(Event_Time_HrGroup),
                               Delta_Time / 60,
                               fill = factor(Event_Time_HrGroup)
                              )
                          ) + 
  geom_boxplot(outlier.colour="red", notch=TRUE) + 
  # coord_cartesian(ylim = c(-300, 1200)) +
  coord_cartesian(ylim = c(-5, 20)) +
  geom_text(data = Count_Values,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 3,
            vjust = -0.5
           ) +
  xlab("Hour Group") + 
  ylab("Bus Lateness (minutes)") + 
  theme(legend.position="none", axis.text.x = element_text(angle=45)) + 
  #theme(legend.position="right", axis.text.x = element_blank()) + 
  ggtitle(expression(atop("How Early/Late is the Bus (by Hour Group)",
                          atop(italic("(positive values are late arrivals)"),
                               ""
                              )
                         )
                    )
         )

DeltTime_BoxPlot

Investigation of Delta_Time (how early or late the bus is).

Exploring “extreme” Delta_Times. First let’s get some “rank” stats.


View(DeltTime_Pctiles)
DeltTime_Pctiles


DeltTimeAbs_Ntile <- as.data.frame(abs(AllDays_NewOrder$Delta_Time)) %>% 
  mutate(Pctile = ntile(abs(AllDays_NewOrder$Delta_Time), 100),
         MinR = min_rank(abs(AllDays_NewOrder$Delta_Time)),
         PctR = percent_rank(abs(AllDays_NewOrder$Delta_Time)),
         PctR_Round = round(PctR, 2)
        ) 

colnames(DeltTimeAbs_Ntile)[1] <- "Delta_Time_Abs"
str(DeltTimeAbs_Ntile)

DeltTimeAbs_Ntile_Rows <- nrow(DeltTimeAbs_Ntile)

View(tail(DeltTimeAbs_Ntile, 500))


DeltTimeAbs_Pctiles <- group_by(DeltTimeAbs_Ntile,
                                PctR_Round
                               ) %>% 
  summarise(
    MinDeltTimeAtPctile = min(Delta_Time_Abs),
    CntsAtPctile = n(),
    PctsAtPctile = CntsAtPctile / DeltTime_Ntile_Rows
  ) %>% 
  mutate(CumSumPAtP = cumsum(PctsAtPctile)
        )

View(DeltTimeAbs_Pctiles)
DeltTimeAbs_Pctiles

Investigation of Delta_Time (how early or late the bus is).

Exploring “extreme” Delta_Times. Then let’s calculate the percentage of buses that are 10 minutes (or more) late/early.


HrGroup_DeltaTime_All <- group_by(AllDays_NewOrder,
                                  Event_Time_HrGroup
                                 ) %>% 
  summarise(EventAll_Cnt = n()
           )

str(HrGroup_DeltaTime_All)
View(HrGroup_DeltaTime_All)


HrGroup_DeltaTime_Above10Min <- filter(AllDays_NewOrder,
                                       abs(Delta_Time) >= 600
                                      ) %>% 
  group_by(Event_Time_HrGroup) %>% 
  summarise(EventAbove10_Cnt = n()
           )

str(HrGroup_DeltaTime_Above10Min)
View(HrGroup_DeltaTime_Above10Min)


HrGroup_DeltaTimeCompare <- inner_join(HrGroup_DeltaTime_Above10Min,
                                       HrGroup_DeltaTime_All,
                                       by = c("Event_Time_HrGroup" = "Event_Time_HrGroup")
                                      ) %>% 
  mutate(PctEventsAbove10 = EventAbove10_Cnt / EventAll_Cnt)

View(HrGroup_DeltaTimeCompare)

Investigation of Delta_Time (how early or late the bus is).

Quickly plot these “extreme” Delta_Times.


DeltTime_Above10_Cols <- ggplot(HrGroup_DeltaTimeCompare,
                                aes(factor(Event_Time_HrGroup),
                                    PctEventsAbove10
                                   )
                               ) +
  geom_col(fill = "lightblue", colour = "grey60", size = 0.2) +
  geom_text(aes(label = format(round(PctEventsAbove10, digits = 2),
                               nsmall = 2
                              )
               ),
            size = 3,
            nudge_y = (HrGroup_DeltaTimeCompare$PctEventsAbove10 * -0.1)
           ) +
  # coord_cartesian(xlim = c(-5, 5)) +
  xlab("Hour Group") + 
  ylab("Percent of All Bus Arrivals") +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  ggtitle(expression(atop("When is a Bus 10+ Minutes Late/Early"
                          # ,atop(italic("positive values are late arrivals"),
                          #      ""
                          #     )
                         )
                    )
         )

DeltTime_Above10_Cols

Quick investigation on the relationship between Dwell_Time2 (the time a bus is at a stop) and Delta_Time (how early/late the bus is).

Correlation.


DwellTDeltaT_Corr <- as.matrix(cor(x = AllDays_NewOrder$Dwell_Time2,
                                   y = AllDays_NewOrder$Delta_Time,
                                   use = "pairwise"
                                  )
                               )

DwellTDeltaT_Corr

Quick investigation on the relationship between Dwell_Time2 (the time a bus is at a stop) and Delta_Time (how early/late the bus is).

Next, let’s get a sample of data for plotting. Let’s do this for the full dataset (AllDays_NewOrder).


AllDays_NewOrder_10PctSamp <- sample_frac(AllDays_NewOrder, 0.1) %>% 
  select(Delta_Time,
         Dwell_Time2
        ) %>% 
  mutate(DataSet = "AllData")

str(AllDays_NewOrder_10PctSamp)

Quick investigation on the relationship between Dwell_Time2 (the time a bus is at a stop) and Delta_Time (how early/late the bus is).

Let’s also get a sample of data for plotting, but with a datset that removes outliers.


View(DeltTime_Pctiles)
View(DwellTime2_Pctiles)

AllDays_NewOrder_NoExtremes_10PctSamp <- filter(AllDays_NewOrder,
                                                between(Delta_Time, -402, 1705) & # removes about 2% of Delta_Time values
                                                  between(Dwell_Time2, 1, 63)  # removes about 2% of Dwell_Time2 values
                                               ) %>% 
  sample_frac(0.1) %>% 
  select(Delta_Time,
         Dwell_Time2
        ) %>% 
  mutate(DataSet = "OutliersRemoved")

str(AllDays_NewOrder_NoExtremes_10PctSamp)

Quick investigation on the relationship between Dwell_Time2 (the time a bus is at a stop) and Delta_Time (how early/late the bus is).

Plotting the data from the dataset that does not remove outliers.


DwellTDeltaT_Scatter <- ggplot(AllDays_NewOrder_10PctSamp,
                               aes(Dwell_Time2, Delta_Time)
                              ) +
  geom_point(shape = 1, alpha = 0.5) +
  scale_shape(solid = FALSE) +
  geom_smooth(method = "lm", colour = "red") +
  # xlab("Time at Stop (sec)") + 
  # ylab("Lateness (sec)") +
  annotate(label = lm_eqn(df = AllDays_NewOrder_10PctSamp,
                          y = AllDays_NewOrder_10PctSamp$Delta_Time,
                          x = AllDays_NewOrder_10PctSamp$Dwell_Time2
                         ),
           x = 2200,
           y = 600,
           geom = "text",
           size = 3,
           colour = "red",
           parse = TRUE
          ) +
  labs(title = "Lateness vs Time at Stop",
       subtitle = "(no outliers removed)",
       x = "Time at Stop (sec)",
       y = "Lateness (sec)"
      )
  # ggtitle(expression(atop("Lateness vs Time at Stop"
  #                         ,atop(italic("(no outliers removed)"),
  #                               ""
  #                              )
  #                        )
  #                   )
  #        )
# +
#   geom_jitter()

DwellTDeltaT_Scatter

Quick investigation on the relationship between Dwell_Time2 (the time a bus is at a stop) and Delta_Time (how early/late the bus is).

Plotting the data from the dataset that does remove outliers.


DwellTDeltaT_Scatter_NoExtremes <- ggplot(AllDays_NewOrder_NoExtremes_10PctSamp,
                                          aes(Dwell_Time2, Delta_Time)
                                         ) +
  geom_point(shape = 1, alpha = 0.5) +
  scale_shape(solid = FALSE) +
  geom_smooth(method = "lm", colour = "blue") +
  # xlab("Time at Stop (sec)") + 
  # ylab("Lateness (sec)") +
  annotate(label = lm_eqn(df = AllDays_NewOrder_NoExtremes_10PctSamp,
                          y = AllDays_NewOrder_NoExtremes_10PctSamp$Delta_Time,
                          x = AllDays_NewOrder_NoExtremes_10PctSamp$Dwell_Time2
                         ),
           x = 50,
           y = -475,
           geom = "text",
           size = 3,
           colour = "blue",
           parse = TRUE
          ) +
  labs(title = "Lateness vs Time at Stop",
       subtitle = "(2% of outliers removed)",
       x = "Time at Stop (sec)",
       y = "Lateness (sec)"
      )
  # ggtitle(expression(atop("Lateness vs Time at Stop"
  #                         ,atop(italic("(2% of outliers removed)"),
  #                               ""
  #                              )
  #                        )
  #                   )
  #        )
# +
#   geom_jitter()

DwellTDeltaT_Scatter_NoExtremes

Quick investigation on the relationship between Dwell_Time2 (the time a bus is at a stop) and Delta_Time (how early/late the bus is).

Plotting the data from both datasets together.


CombinedData <- rbind(AllDays_NewOrder_10PctSamp,
                      AllDays_NewOrder_NoExtremes_10PctSamp
                     )

CombinedData$DataSet <- factor(CombinedData$DataSet)

str(CombinedData)


DwellTDeltaT_Scatter_Combined <- ggplot(CombinedData,
                                        aes(x = Dwell_Time2,
                                            y = Delta_Time,
                                            colour = DataSet
                                           )
                                       ) +
  geom_point(shape = 1, alpha = 0.5) +
  scale_shape(solid = FALSE) +
  coord_cartesian(xlim = c(0, 500), ylim = c(-1000, 2000)
                 ) +
  geom_smooth(data = filter(CombinedData,
                            DataSet == "AllData"
                           ),
              method = "lm",
              colour = "red"
             ) +
  geom_smooth(data = filter(CombinedData,
                            DataSet == "OutliersRemoved"
                           ),
              method = "lm",
              colour = "blue"
             ) +
  # facet_wrap( ~ DataSet, ncol = 2) +
  annotate(label = lm_eqn(df = AllDays_NewOrder_10PctSamp,
                          y = AllDays_NewOrder_10PctSamp$Delta_Time,
                          x = AllDays_NewOrder_10PctSamp$Dwell_Time2
                         ),
           x = 300,
           y = -600,
           geom = "text",
           size = 3,
           colour = "red",
           parse = TRUE
          ) +
  annotate(label = lm_eqn(df = AllDays_NewOrder_NoExtremes_10PctSamp,
                          y = AllDays_NewOrder_NoExtremes_10PctSamp$Delta_Time,
                          x = AllDays_NewOrder_NoExtremes_10PctSamp$Dwell_Time2
                         ),
           x = 300,
           y = -800,
           geom = "text",
           size = 3,
           colour = "blue",
           parse = TRUE
          ) +
  theme(legend.position = "bottom") +
  labs(title = "Lateness vs Time at Stop",
       x = "Time at Stop (sec)",
       y = "Lateness (sec)"
      )
  # ggtitle(expression(atop("Lateness vs Time at Stop"
                          # ,atop(italic("2% of outliers removed"),
                          #       ""
                          #      )
         #                 )
         #            )
         # )
# +
#   geom_jitter()

DwellTDeltaT_Scatter_Combined

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Cmd+Option+I.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Cmd+Shift+K to preview the HTML file).

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayBmb3IgV01BVEEgTWV0cm9idXMgRGF0YSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICBodG1sX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2sgZm9yIGFuYWx5c2lzIHVzaW5nIGRhdGEgb24gdGhlIERDIEJ1cyBTeXN0ZW0gKFdNQVRBIE1ldHJvYnVzKS4gIFRoZSBkYXRhIHdlcmUgb2J0YWluZWQgaGVyZToKCmh0dHBzOi8vcGxhbml0bWV0cm8uY29tLzIwMTYvMTEvMTYvZGF0YS1kb3dubG9hZC1tZXRyb2J1cy12ZWhpY2xlLWxvY2F0aW9uLWRhdGEvCgoKTG9hZCB0aGUgcGFja2FnZXMgdG8gYmUgdXNlZC4KYGBge3IgZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CgpsaWJyYXJ5KCJqc29ubGl0ZSIpICAgICAgICAgICAjIG1hbmlwdWxhdGluZyBKU09OIGZpbGVzIGZvciB6aXAgY29kZXMKbGlicmFyeSgic3FsZGYiKSAgICAgICAgICAgICAgIyBzcWwtYmFzZWQgZGF0YSBtYW5pcHVsYXRpb24KbGlicmFyeSgidGNsdGsiKQpsaWJyYXJ5KCJ0aWR5ciIpICAgICAgICAgICAgICAjIGRhdGEgbWFuaXB1bGF0aW9uCmxpYnJhcnkoInBseXIiKSAgICAgICAgICAgICAgICMgZGF0YSBtYW5pcHVsYXRpb24KbGlicmFyeSgiZHBseXIiKSAgICAgICAgICAgICAgIyBkYXRhIG1hbmlwdWxhdGlvbgpsaWJyYXJ5KCJtYWdyaXR0ciIpICAgICAgICAgICAjIGRhdGEgbWFuaXB1bGF0aW9uIChwaXBpbmcgZGF0YSkKbGlicmFyeSgic3RyaW5nciIpICAgICAgICAgICAgIyBzdHJpbmcgbWFuaXB1bGF0aW9uCmxpYnJhcnkoImRhdGEudGFibGUiKSAgICAgICAgICMgdXNlZCBpbiB0ZXN0aW5nIGRhdGEgbWFuaXB1bGF0aW9uIGZvciBzcGVlZCBpbmNyZWFzZXMKbGlicmFyeSgibHVicmlkYXRlIikgICAgICAgICAgIyBkYXRlIG1hbmlwdWxhdGlvbgpsaWJyYXJ5KCJnZW9zcGhlcmUiKSAgICAgICAgICAjIGNhbGN1bGF0aW5nIEhhdmVyc2luZSBkaXN0YW5jZQpsaWJyYXJ5KCJnZ3Bsb3QyIikgICAgICAgICAgICAjIGdlbmVyYWwgcGxvdHRpbmcKbGlicmFyeSgiZ2d2aXMiKSAgICAgICAgICAgICAgIyBnZW5lcmFsIHBsb3R0aW5nCmxpYnJhcnkoInJib2tlaCIpICAgICAgICAgICAgICMgZ2VuZXJhbCBwbG90dGluZwpsaWJyYXJ5KCJnZ21hcCIpICAgICAgICAgICAgICAjIGdlbmVyYWwgcGxvdHRpbmcgb2YgbWFwcwpsaWJyYXJ5KCJyZ2RhbCIpICAgICAgICAgICAgICAjIHVzZWQgaW4gcGxvdHRpbmcgc2hhcGVmaWxlcwpsaWJyYXJ5KCJicm9vbSIpICAgICAgICAgICAgICAjIHVzZWQgaW4gcGxvdHRpbmcgc2hhcGVmaWxlcwpsaWJyYXJ5KCJtYXB0b29scyIpICAgICAgICAgICAjIHVzZWQgaW4gcGxvdHRpbmcgc2hhcGVmaWxlcwpsaWJyYXJ5KCJyZ2VvcyIpICAgICAgICAgICAgICAjIHVzZWQgaW4gcGxvdHRpbmcgc2hhcGVmaWxlcwpsaWJyYXJ5KCJjYXJldCIpICAgICAgICAgICAgICAjIHVzZWQgaW4gUENBCmxpYnJhcnkoImNsdXN0ZXIiKSAgICAgICAgICAgICMgdXNlZCBmb3IgY2x1c3RlcmluZwpsaWJyYXJ5KCJmcGMiKSAgICAgICAgICAgICAgICAjIHVzZWQgZm9yIGNsdXN0ZXJpbmcKbGlicmFyeSgiZGJzY2FuIikgICAgICAgICAgICAgIyB1c2VkIGZvciBjbHVzdGVyaW5nCmxpYnJhcnkoIk5iQ2x1c3QiKSAgICAgICAgICAgICMgdXNlZCBmb3IgY2x1c3RlcmluZwpsaWJyYXJ5KCJmYWN0b2V4dHJhIikgICAgICAgICAjIHBsb3R0aW5nIGNsdXN0ZXJzCiMgbGlicmFyeSgiZ3BjbGliIikKIyBpbnN0YWxsLnBhY2thZ2VzKCdyZ2VvcycsIHR5cGU9J3NvdXJjZScpCiMgaW5zdGFsbC5wYWNrYWdlcygncmdkYWwnLCB0eXBlPSdzb3VyY2UnKQojIGluc3RhbGwucGFja2FnZXMoIk5iQ2x1c3QiKQoKYGBgCgoKR2V0IHRoZSBCdXMgZGF0YS4KCkZpcnN0IGxldCdzIGNoZWNrIHRoZSB3b3JraW5nIGRpcmVjdG9yeS4KYGBge3J9CgpnZXR3ZCgpCgpgYGAKCgpUaGVuLCBhY3R1YWxseSBnZXQgdGhlIGRhdGEuCmBgYHtyIGVjaG8gPSBGQUxTRX0KCnNldHdkKCIvVXNlcnMvbWR0dXJzZS9EZXNrdG9wL0FuYWx5dGljcy9EQ01ldHJvQnVzL0J1cyBBVkwgT2N0IDIwMTYiKQoKZm9yIChpIGluIDM6Nyl7CiAgYXNzaWduKHBhc3RlMCgiT2N0MCIsIGksICJSYXciKSwKICAgICAgICAgcmVhZC5kZWxpbShwYXN0ZTAoIjIwMTYxMDAiLCBpLCAiTWV0cm9idXNBVkwudHh0IiksCiAgICAgICAgICAgICAgICAgICAgc2VwID0gIlx0IiwKICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgIG5hLnN0cmluZ3MgPSBOVUxMCiAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKQogIAogIG1lc3NhZ2UoIk9jdDAiLCBpLCAiUmF3IikKIAogIHN0cihnZXQocGFzdGUwKCJPY3QwIiwgaSwgIlJhdyIpCiAgICAgICAgICkKICAgICApCiAgfQoKYGBgCgoKUHV0IHRoZSBkYWlseSBkYXRhIHRvZ2V0aGVyLgpgYGB7cn0KCkFsbERheXMgPC0gYmluZF9yb3dzKGxpc3QoT2N0MDNSYXcsIE9jdDA0UmF3LCBPY3QwNVJhdywgT2N0MDZSYXcsIE9jdDA3UmF3KSwKICAgICAgICAgICAgICAgICAgICAgLmlkID0gYygiZ3JvdXAiKQogICAgICAgICAgICAgICAgICAgICkKIyBkaW0oQWxsRGF5cykKc3RyKEFsbERheXMpCgpgYGAKCgpEZWxldGluZyBvbGQgZGF0YSBmcmFtZXMuCmBgYHtyfQoKZm9yIChpIGluIDM6Nyl7CiAgcm0obGlzdCA9IGxzKHBhdHRlcm4gPSBwYXN0ZTAoIk9jdDAiLCBpLCAiUmF3IikKICAgICAgICAgICAgICApCiAgICApCiAgCiAgbWVzc2FnZSgiRGVsZXRpbmcgT2N0MCIsIGksICJSYXciKQogIH0KCmBgYAoKClVwZGF0aW5nIHZhcmlhYmxlIHR5cGVzLgoKVGhlbiwgc29ydGluZyB0aGUgZGF0YSBhbmQgYWRkaW5nIGEgUm93TnVtYmVyICh0byBiZSB1c2VkIGZvciBpZGVudGlmeWluZyByb3dzIGxhdGVyIGluIHRoZSBhbmFseXNlcy4pCmBgYHtyfQoKcm0oaSkKCgpBbGxEYXlzJGdyb3VwIDwtIGZhY3RvcihBbGxEYXlzJGdyb3VwKQpBbGxEYXlzJFJvdXRlX0RpcmVjdGlvbiA8LSBmYWN0b3IoQWxsRGF5cyRSb3V0ZV9EaXJlY3Rpb24pCkFsbERheXMkRXZlbnRfVGltZSA8LSBhcy5QT1NJWGN0KEFsbERheXMkRXZlbnRfVGltZSwgZm9ybWF0ID0gIiVtLSVkLSV5ICVJOiVNOiVTICVwIikKQWxsRGF5cyREZXBhcnR1cmVfVGltZSA8LSBhcy5QT1NJWGN0KEFsbERheXMkRGVwYXJ0dXJlX1RpbWUsIGZvcm1hdCA9ICIlbS0lZC0leSAlSTolTTolUyAlcCIpCgpzdHIoQWxsRGF5cykKCgpBbGxEYXlzX1NvcnRlZCA8LSBhcnJhbmdlKEFsbERheXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgQnVzX0lELAogICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWUKICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIG11dGF0ZShSb3dOdW1fT0cgPSByb3dfbnVtYmVyKCkgIyB0aGlzIGlzIHVzZWZ1bCBpbiBpZGVudGlmeSB0aGUgcm93IGxhdGVyIG9uCiAgICAgICAgKQoKcm0oQWxsRGF5cykKc3RyKEFsbERheXNfU29ydGVkKQoKIyBWaWV3KGhlYWQoQWxsRGF5c19Tb3J0ZWQsIDEwMCkpCgpgYGAKCgpJbnNwZWN0aW5nIHRoZSB2YWx1ZXMgb2YgU3RvcF9JRCwgYW5kIGZpbmRpbmcgdGhhdCBpdCBjYW4gdGFrZSB0aGUgdmFsdWVzICIiIChibGFuaykgYW5kICJOVUxMIi4KYGBge3J9CgpWaWV3KGdyb3VwX2J5KEFsbERheXNfU29ydGVkLAogICAgICAgICAgICAgIFN0b3BfSUQKICAgICAgICAgICAgICkgJT4lIAogICAgICAgc3VtbWFyaXNlKAogICAgICAgICBDbnQgPSBuKCkKICAgICAgICAgKSAlPiUgCiAgICAgICBhcnJhbmdlKFN0b3BfSUQpCiAgICApCgpWaWV3KGZpbHRlcihBbGxEYXlzX1NvcnRlZCwKICAgICAgICAgICAgaXMubmEoU3RvcF9JRCkgfAogICAgICAgICAgICAgIFN0b3BfSUQgPT0gIiIgfAogICAgICAgICAgICAgIFN0b3BfSUQgPT0gIk5VTEwiCiAgICAgICAgICAgKSAlPiUgCiAgICAgICBhcnJhbmdlKFN0b3BfRGVzYykKICAgICkKCmBgYAoKCkNyZWF0aW5nIGEgdGFibGUgb2YgZGlzdGluY3QgU3RvcF9EZXNjIHZhbHVlcyB3aGVuIFN0b3BfSUQgaXMgIiIgKGJsYW5rKSBvciAiTlVMTCIuCmBgYHtyfQoKU3RvcElEX05ldyA8LSBmaWx0ZXIoQWxsRGF5c19Tb3J0ZWQsCiAgICAgICAgICAgICAgICAgICAgIGlzLm5hKFN0b3BfSUQpIHwKICAgICAgICAgICAgICAgICAgICAgICBTdG9wX0lEID09ICIiIHwKICAgICAgICAgICAgICAgICAgICAgICBTdG9wX0lEID09ICJOVUxMIgogICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHNlbGVjdChTdG9wX0lELCBTdG9wX0Rlc2MpICU+JSAKICBkaXN0aW5jdCgpICU+JSAKICBhcnJhbmdlKFN0b3BfSUQsIFN0b3BfRGVzYykgJT4lIAogIG11dGF0ZShTdG9wSURfTmV3ID0gMTpucm93KC4pCiAgICAgICAgKQoKVmlldyhTdG9wSURfTmV3KQpTdG9wSURfTmV3CgpgYGAKCgpDcmVhdGluZyBhIGZ1bGwgdXBkYXRlZCB0YWJsZSBieSBmaWxsaW5nIGluIFN0b3BJRF9OZXcgZm9yIHdoZW4gU3RvcF9JRCBpcyAiIiAoYmxhbmspIG9yIE5VTEwuCmBgYHtyfQoKQWxsRGF5c19TdG9wSUROZXcgPC0gbGVmdF9qb2luKEFsbERheXNfU29ydGVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KFN0b3BJRF9OZXcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RvcF9EZXNjLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0b3BJRF9OZXcKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IGMoIlN0b3BfRGVzYyIgPSAiU3RvcF9EZXNjIikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFN0b3BJRF9DbGVhbiA9IGlmZWxzZShpcy5uYShTdG9wSURfTmV3KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0b3BfSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdG9wSURfTmV3CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFN0b3BJRF9JbmRpY2F0b3IgPSBmYWN0b3IoaWZlbHNlKGlzLm5hKFN0b3BJRF9OZXcpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSURfT0siLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSURfQmFkIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICApCgpybShTdG9wSURfTmV3KQpybShBbGxEYXlzX1NvcnRlZCkKc3RyKEFsbERheXNfU3RvcElETmV3KQoKIyBWaWV3KHRhaWwoQWxsRGF5c19TdG9wSUROZXcsIDUwMCkpCiMgVmlldyhmaWx0ZXIoQWxsRGF5c19TdG9wSUROZXcsCiMgICAgICAgICAgICAgU3RvcF9EZXNjID09ICJNRVRST1dBWSBBTk5OT1VDRU1OVCBDT1JSIgojICAgICAgICAgICAgKQojICAgICApCgpgYGAKCgpMYXQgTG9uZyBzdGF0cyBmb3IgcHVsbGluZyBpbiBaaXAgY29kZXMgbGF0ZXIuCmBgYHtyfQoKTExfU3RhdHMgPC0gZ3JvdXBfYnkoQWxsRGF5c19TdG9wSUROZXcsCiAgICAgICAgICAgICAgICAgICAgIFN0b3BJRF9DbGVhbgogICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHN1bW1hcmlzZShMYXRfTWVhbiA9IG1lYW4oTGF0aXR1ZGUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIExhdF9NZWQgPSBtZWRpYW4oTGF0aXR1ZGUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIExuZ19NZWFuID0gbWVhbihMb25naXR1ZGUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIExuZ19NZWQgPSBtZWRpYW4oTG9uZ2l0dWRlLCBuYS5ybSA9IFRSVUUpCiAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKExhdF9NZWFMZXNzTWVkID0gTGF0X01lYW4gLSBMYXRfTWVkLAogICAgICAgICBMbmdfTWVhTGVzc01lZCA9IExuZ19NZWFuIC0gTG5nX01lZCwKICAgICAgICAgUm93TnVtID0gcm93X251bWJlcigpCiAgICAgICAgKQoKc3RyKExMX1N0YXRzKQpzdW1tYXJ5KExMX1N0YXRzKQoKVmlldyhoZWFkKGFycmFuZ2UoTExfU3RhdHMsCiAgICAgICAgICAgICAgICAgIExhdF9NZWFMZXNzTWVkCiAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgIDUwMAogICAgICAgICApCiAgICApCgpWaWV3KGhlYWQoYXJyYW5nZShMTF9TdGF0cywKICAgICAgICAgICAgICAgICAgZGVzYyhMYXRfTWVhTGVzc01lZCkKICAgICAgICAgICAgICAgICApLAogICAgICAgICAgNTAwCiAgICAgICAgICkKICAgICkKClZpZXcoaGVhZChhcnJhbmdlKExMX1N0YXRzLAogICAgICAgICAgICAgICAgICBMbmdfTWVhTGVzc01lZAogICAgICAgICAgICAgICAgICksCiAgICAgICAgICA1MDAKICAgICAgICAgKQogICAgKQoKVmlldyhoZWFkKGFycmFuZ2UoTExfU3RhdHMsCiAgICAgICAgICAgICAgICAgIGRlc2MoTG5nX01lYUxlc3NNZWQpCiAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgIDUwMAogICAgICAgICApCiAgICApCgpgYGAKCgpQdWxsaW5nIGluIFppcCBDb2RlIGRhdGEgZnJvbSBhcGkuZ2VvbmFtZXMub3JnLgpgYGB7cn0KCiMgVVJMIEVYQU1QTEU6CiMgaHR0cDovL2FwaS5nZW9uYW1lcy5vcmcvZmluZE5lYXJieVBvc3RhbENvZGVzSlNPTj9sYXQ9MzguODk1NjAmbG5nPS03Ni45NDg3MyZyYWRpdXM9MCZ1c2VybmFtZT1zdXBlcm1kYXQKCnVybF8xIDwtICJodHRwOi8vYXBpLmdlb25hbWVzLm9yZy9maW5kTmVhcmJ5UG9zdGFsQ29kZXNKU09OP2xhdD0iCnVybF8yIDwtICImbG5nPSIKdXJsXzMgPC0gIiZyYWRpdXM9MCZ1c2VybmFtZT0iCnVzZXJuYW1lIDwtICJzdXBlcm1kYXQiCgoKIyBuZWVkIHRvIGdyb3VwIGluIGJ1bmNoZXMgYXMgaHR0cDovL2FwaS5nZW9uYW1lcy5vcmcgbGltaXRzIHB1bGxzIHRvIDIwMDAgcGVyIGhvdXIKCgojIyMjIyBTdG9yZSBldmVyeXRoaW5nIGluIG11bHRpcGxlIGxpc3RzCnBhZ2VzMSA8LSBsaXN0KCkKCgpzeXN0ZW0udGltZSgKCmZvcihpIGluIDE6MTAwMCl7CiAgbGF0IDwtIGZpbHRlcihMTF9TdGF0cywKICAgICAgICAgICAgICAgIFJvd051bSA9PSBpCiAgICAgICAgICAgICAgICkgJT4lCiAgICBzZWxlY3QoTGF0X01lZCkKICAKICBsbmcgPC0gZmlsdGVyKExMX1N0YXRzLAogICAgICAgICAgICAgICAgUm93TnVtID09IGkKICAgICAgICAgICAgICAgKSAlPiUKICAgIHNlbGVjdChMbmdfTWVkKQogIAogIEFQSURhdGExIDwtIGZyb21KU09OKHBhc3RlMCh1cmxfMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmxfMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG5nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmxfMywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXNlcm5hbWUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgIGZsYXR0ZW4gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICApCiAgCiAgbWVzc2FnZSgiUmV0cmlldmluZyBaaXAgQ29kZSAiLCBpKQogIAogIHBhZ2VzMVtbaV1dIDwtIEFQSURhdGExJHBvc3RhbENvZGVzCiAgCn0KKQoKCiMjIyMjIENvbWJpbmUgdGhlIGxpc3RzIGludG8gb25lIHBhZ2UKWmlwczEgPC0gcmJpbmQucGFnZXMocGFnZXMxW3NhcHBseShwYWdlczEsIGxlbmd0aCkgPiAwXSkKCgojIyMjIyBDb21iaW5lIGFsbCBwYWdlcwpaaXBzX0FsbCA8LSBiaW5kX3Jvd3MoWmlwczAsCiAgICAgICAgICAgICAgICAgICAgICBaaXBzMSwKICAgICAgICAgICAgICAgICAgICAgIFppcHMyLAogICAgICAgICAgICAgICAgICAgICAgWmlwczMsCiAgICAgICAgICAgICAgICAgICAgICBaaXBzNCwKICAgICAgICAgICAgICAgICAgICAgIFppcHM1LAogICAgICAgICAgICAgICAgICAgICAgWmlwczYsCiAgICAgICAgICAgICAgICAgICAgICBaaXBzNywKICAgICAgICAgICAgICAgICAgICAgIFppcHM4LAogICAgICAgICAgICAgICAgICAgICAgWmlwczksCiAgICAgICAgICAgICAgICAgICAgICBaaXBzMTAsCiAgICAgICAgICAgICAgICAgICAgICAuaWQgPSAiaWQiCiAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIG11dGF0ZShVbmlxdWVMYXRMbmcgPSBwYXN0ZShsYXQsIGxuZywgc2VwID0gIl9fIikKICAgICAgICApCgojIHN0cihaaXBzX0FsbCkKIyBWaWV3KGhlYWQoWmlwc19BbGwpKQoKCiMgc3RyKExMX1N0YXRzKQpMTF9TdGF0c19VbnFMYXRMbmcgPC0gbXV0YXRlKExMX1N0YXRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFVuaXF1ZUxhdExuZyA9IHBhc3RlKExhdF9NZWQsIExuZ19NZWQsIHNlcCA9ICJfXyIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgojIHN0cihMTF9TdGF0c19VbnFMYXRMbmcpCiMgVmlldyhoZWFkKExMX1N0YXRzX1VucUxhdExuZykpCgoKTExfU3RhdHNaaXBzIDwtIGxlZnRfam9pbihMTF9TdGF0c19VbnFMYXRMbmcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgWmlwc19BbGwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCJVbmlxdWVMYXRMbmciID0gIlVuaXF1ZUxhdExuZyIpCiAgICAgICAgICAgICAgICAgICAgICAgICApCgpzdHIoTExfU3RhdHNaaXBzKQojIFZpZXcoaGVhZChMTF9TdGF0c1ppcHMpKQoKIyBOb3Qgc3VyZSB3aGV5IHRoZXNlIGNvdWxkbid0IGJlIGZvdW5kICh3aHkgdGhleSdyZSBOQSkKVmlldyhmaWx0ZXIoTExfU3RhdHNaaXBzLAogICAgICAgICAgICBpcy5uYShwb3N0YWxDb2RlKQogICAgICAgICAgICkKICAgICkKCmBgYAoKCkpvaW4gdG8gY3JlYXRlIG9uZSBkYXRhc2V0IHRoYXQgYWxzbyBpbmNsdWRlcyBaaXAgdmFyaWFibGVzLgpgYGB7cn0KCnJtKHVybF8xLCB1cmxfMiwgdXJsXzMsIHVzZXJuYW1lLCBwYWdlczAsIHBhZ2VzMSwgcGFnZXMyLCBwYWdlczMsIHBhZ2VzNCwgcGFnZXM1LCBwYWdlczYsIHBhZ2VzNywgcGFnZXM4LCBwYWdlczksIHBhZ2VzMTAsIGksIGxhdCwgbG5nLCBBUElEYXRhMCwgQVBJRGF0YTEsIEFQSURhdGEyLCBBUElEYXRhMywgQVBJRGF0YTQsIEFQSURhdGE1LCBBUElEYXRhNiwgQVBJRGF0YTcsIEFQSURhdGE4LCBBUElEYXRhOSwgQVBJRGF0YTEwLCBMTF9TdGF0cywgTExfU3RhdHNfVW5xTGF0TG5nKQoKCkFsbERheXNfWmlwcyA8LSBsZWZ0X2pvaW4oQWxsRGF5c19TdG9wSUROZXcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgTExfU3RhdHNaaXBzLAogICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygiU3RvcElEX0NsZWFuIiA9ICJTdG9wSURfQ2xlYW4iKQogICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgcmVuYW1lKFN0b3BfU3RhdGUgPSBhZG1pbkNvZGUxLAogICAgICAgICBTdG9wX0NvdW50eSA9IGFkbWluTmFtZTIsCiAgICAgICAgIFN0b3BfQ2l0eSA9IHBsYWNlTmFtZSwKICAgICAgICAgU3RvcF9aaXAgPSBwb3N0YWxDb2RlCiAgICAgICAgICkKCnJtKEFsbERheXNfU3RvcElETmV3LCBMTF9TdGF0c1ppcHMpCnN0cihBbGxEYXlzX1ppcHMpCgpgYGAKCgpVcGRhdGluZyB2YXJpYWJsZSB0eXBlcy4KYGBge3J9CgpBbGxEYXlzX1ppcHMkU3RvcF9TdGF0ZSA8LSBmYWN0b3IoQWxsRGF5c19aaXBzJFN0b3BfU3RhdGUpCkFsbERheXNfWmlwcyRTdG9wX0NvdW50eSA8LSBmYWN0b3IoQWxsRGF5c19aaXBzJFN0b3BfQ291bnR5KQpBbGxEYXlzX1ppcHMkU3RvcF9aaXAgPC0gZmFjdG9yKEFsbERheXNfWmlwcyRTdG9wX1ppcCkKQWxsRGF5c19aaXBzJFN0b3BfQ2l0eSA8LSBmYWN0b3IoQWxsRGF5c19aaXBzJFN0b3BfQ2l0eSkKCkFsbERheXNfWmlwcyRkaXN0YW5jZSA8LSBhcy5udW1lcmljKEFsbERheXNfWmlwcyRkaXN0YW5jZSkKQWxsRGF5c19aaXBzJGNvdW50cnlDb2RlIDwtIGZhY3RvcihBbGxEYXlzX1ppcHMkY291bnRyeUNvZGUpCkFsbERheXNfWmlwcyRhZG1pbk5hbWUxIDwtIGZhY3RvcihBbGxEYXlzX1ppcHMkYWRtaW5OYW1lMSkKCnN0cihBbGxEYXlzX1ppcHMpCgpgYGAKCgpGZWF0dXJlIGVuZ2luZWVyaW5nLgoKSW5zcGVjdGluZyBpbmNpZGVuY2VzIG9mIGNvbnNlY3V0aXZlIFN0b3BfSURzLiBUaGlzIGlzIGRvbmUgYmVjYXVzZSBpbnZlc3RpZ2F0aW9uIHNob3dlZCB0aGF0IG1hbnkgY29uc2V1dGl2ZSBldmVudHMgb2NjdXJyIGF0IHRoZSBzYW1lIFN0b3BfSUQsIGJ1dCB3aXRoIHZhcmlvdXMgRHdlbGxfVGltZXMsIE9kb21ldGVyX0Rpc3RhbmNlcywgZXRjLiAgQWxsIG9mIHdoaWNoIGFmZmVjdCBjYWxjdWxhdGlvbnMgYW5kIGFuYWx5c2VzLgoKQ3JlYXRlIGRhdGEgb24gdGhlIHJ1bnMgKGNvbnNlY3V0aXZlIFN0b3BfSURzKS4KYGBge3J9CgpTdG9wSURfUnVucyA8LSBybGUoQWxsRGF5c19aaXBzJFN0b3BJRF9DbGVhbikKClN0b3BJRF9SdW5zJGVuZHMgPC0gY3Vtc3VtKFN0b3BJRF9SdW5zJGxlbmd0aHMpCgpTdG9wSURfUnVucyRzdGFydHMgPC0gaWZlbHNlKGlzLm5hKGxhZyhTdG9wSURfUnVucyRlbmRzKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhZyhTdG9wSURfUnVucyRlbmRzKSArIDEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCnN0cihTdG9wSURfUnVucykKIyBjbGFzcyhTdG9wSURfUnVucykKIyAKIyBTdG9wSURfUnVuc19kZiA8LSBkYXRhLmZyYW1lKHVuY2xhc3MoU3RvcElEX1J1bnMpKQojIHN0cihTdG9wSURfUnVuc19kZikKIyBjbGFzcyhTdG9wSURfUnVuc19kZikKIyBybShTdG9wSURfUnVuc19kZikKCmBgYAoKClRyeWluZyB0byBsaW5rIGRhdGEgb24gUnVuc0dyb3VwcyB3aXRoIHRoZSBvcmlnaW5hbCBkYXRhIChBbGxEYXlzX1NvcnRlZCkuIFRoZSBnb2FsIGlzIHRvIHNlbGVjdCBvbmx5IG9uZSByZWNvcmQgcGVyIFJ1bnNHcm91cCAtIHRoYXQgYmVpbmcgdGhlIHJlY29yZCB3aXRoIHRoZSBsb25nZXN0IER3ZWxsX1RpbWUuCgpJIGF0dGVtcHRlZCB0aGlzIGNvbXB1dGF0aW9uIHVzaW5nIGJvdGggZGF0YS5mcmFtZXMgKGRwbHlyKSBhbmQgZGF0YS50YWJsZXMgKGRhdGEudGFibGUpLiBIb3dldmVyLCB3aXRoIDIsODA5LDA2MiByb3dzIGluIG9uZSBkYXRhc2V0IGFuZCAzLDExOSw0NDMgcm93cyBpbiB0aGUgb3RoZXIgZGF0YXNldCwgdGhlIGN1cnJlbnQgY29tcHV0YXRpb24gdGltZSBpcyBvdmVyIDUgZGF5cy4uLnNvIEknbSB0cnlpbmcgYSBkaWZmZXJlbnQgc3RyYXRlZ3kgdG8gb25seSBzZWxlY3QgdGhlIGZpcnN0IHJlY29yZCBpbiBhIHJ1bi4KYGBge3J9CgojIENyZWF0ZSBhIFJ1bnNHcm91cCB2YXJpYWJsZSBmb3IgZWFjaCBydW4KIyBTdG9wSURfUnVuc19kZiRSdW5zR3JvdXAgPC0gcGFzdGUwKCJnIiwgc2VxKDE6bnJvdyhTdG9wSURfUnVuc19kZikKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiMgCiMgc3RyKFN0b3BJRF9SdW5zX2RmKQojIGhlYWQoU3RvcElEX1J1bnNfZGYsIDI1KQojIHRhaWwoU3RvcElEX1J1bnNfZGYsIDI1KQojIAojIFN0b3BJRF9SdW5zX2RmIDwtIFN0b3BJRF9SdW5zX2RmICU+JSAKIyAgIG11dGF0ZShSb3dOdW0gPSByb3dfbnVtYmVyKCkKIyAgICAgICAgICkKIyAKIyBzdHIoU3RvcElEX1J1bnNfZGYpCiMgaGVhZChTdG9wSURfUnVuc19kZiwgMjUpCiMgdGFpbChTdG9wSURfUnVuc19kZiwgMjUpCiMgCiMgCiMgIyBDb252ZXJ0aW5nIHRvIGRhdGEudGFibGVzIGZvciwgaG9wZWZ1bGx5LCBpbXByb3ZlZCBwZXJmb3JtYW5jZSAoc3BlZWQpIGluIGNvbXB1dGF0aW9uCiMgU3RvcElEX1J1bnNfZHQgPC0gZGF0YS50YWJsZShTdG9wSURfUnVuc19kZikKIyBzZXRrZXkoU3RvcElEX1J1bnNfZHQsIFJvd051bSkKIyBzdHIoU3RvcElEX1J1bnNfZHQpCiMgCiMgQWxsRGF5c19Tb3J0ZWRfZHQgPC0gZGF0YS50YWJsZShBbGxEYXlzX1NvcnRlZCkKIyBzZXRrZXkoQWxsRGF5c19Tb3J0ZWRfZHQsIFJvd051bV9PRykKIyBzdHIoQWxsRGF5c19Tb3J0ZWRfZHQpCiMgIyBybShBbGxEYXlzX1NvcnRlZF9kdCkKIyAKIyAKIyAjIEFjdHVhbCBsb29wIHRvIHBlcmZvcm0gdGhlIGNvbXB1dGF0aW9ucyBhbmQgbGluayB0byBvcmlnaW5hbCBkYXRhIChBbGxEYXlzX1NvcnRlZF9kdCkKIyBHcm91cERhdGEgPC0gbGlzdCgpCiMgZm9yKGkgaW4gMTpucm93KFN0b3BJRF9SdW5zX2R0KQojICAgICkgewojICAgYXNzaWduKHBhc3RlMCgiZ3JvdXBfIiwgaSksCiMgICAgICAgICAgICBTdG9wSURfUnVuc19kdFtSb3dOdW0gPT0gaSwgUnVuc0dyb3VwXQojICAgICAgICAgICApCiMgCiMgICAgICMjIyMjICBUaGUgY29kZSBiZWxvdyBpcyB0aGUgc2FtZSBjb2RlIGFzIGFib3ZlLCBidXQgZG9uZSB3aXRoIGRwbHlyICAjIyMjIwojIAojICAgICAjIGFzc2lnbihwYXN0ZTAoImdyb3VwXyIsIGkpLAojICAgIyAgICAgICAgZmlsdGVyKFN0b3BJRF9SdW5zX2RmLAojICAgIyAgICAgICAgICAgICAgIFJvd051bSA9PSBpCiMgICAjICAgICAgICAgICAgICApICU+JSAKIyAgICMgICAgICAgICAgc2VsZWN0KFJ1bnNHcm91cCkKIyAgICMgICAgICAgKQojIAojICAgYXNzaWduKHBhc3RlMCgiZ3JvdXBfIiwgaSwgIl9zdGFydCIpLAojICAgICAgICAgIFN0b3BJRF9SdW5zX2R0W1Jvd051bSA9PSBpLCBzdGFydHNdCiMgICAgICAgICApCiMgCiMgICBhc3NpZ24ocGFzdGUwKCJncm91cF8iLCBpLCAiX2VuZCIpLAojICAgICAgICAgIFN0b3BJRF9SdW5zX2R0W1Jvd051bSA9PSBpLCBlbmRzXQojICAgICAgICAgKQojIAojICAgYXNzaWduKHBhc3RlMCgiZ3JvdXBfIiwgaSwgIl9yb3dzIiksCiMgICAgICAgICAgQWxsRGF5c19Tb3J0ZWRfZHRbUm93TnVtX09HID49IGFzLm51bWVyaWMoZ2V0KHBhc3RlMCgiZ3JvdXBfIiwgaSwgIl9zdGFydCIpCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAmCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgUm93TnVtX09HIDw9IGFzLm51bWVyaWMoZ2V0KHBhc3RlMCgiZ3JvdXBfIiwgaSwgIl9lbmQiKQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgUnVuc0dyb3VwIDo9IGFzLmNoYXJhY3RlcihnZXQocGFzdGUwKCJncm91cF8iLCBpKQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiMgICAgICAgICAgICAgICAgICAgICAgICAgICBdCiMgCiMgICAgICMjIyMjICBUaGUgY29kZSBiZWxvdyBpcyB0aGUgc2FtZSBhcyB0aGUgY29kZSBhYm92ZSwgYnV0IGRvbmUgd2l0aCBkcGx5ciAgIyMjIyMKIyAKIyAgICAgICAgICAjIGZpbHRlcihBbGxEYXlzX1NvcnRlZCwKIyAgICAgICAgICAjICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywKIyAgICAgICAgICAjICAgICAgICAgICAgICAgIGFzLm51bWVyaWMoZ2V0KHBhc3RlMCgiZ3JvdXBfIiwgaSwgIl9zdGFydCIpCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKIyAgICAgICAgICAjICAgICAgICAgICAgICAgICAgICAgICAgICApLAojICAgICAgICAgICMgICAgICAgICAgICAgICAgYXMubnVtZXJpYyhnZXQocGFzdGUwKCJncm91cF8iLCBpLCAiX2VuZCIpCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKIyAgICAgICAgICAjICAgICAgICAgICAgICAgICAgICAgICAgICApCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICkKIyAgICAgICAgICAjICAgICAgICkgJT4lIAojICAgICAgICAgICMgICBtdXRhdGUoUnVuc0dyb3VwID0gYXMuY2hhcmFjdGVyKGdldChwYXN0ZTAoImdyb3VwXyIsIGkpCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKIyAgICAgICAgICAjICAgICAgICApCiMgICAgICAgICApCiMgCiMgICBHcm91cERhdGFbW2ldXSA8LSBnZXQocGFzdGUwKCJncm91cF8iLCBpLCAiX3Jvd3MiKSkKIyAKIyAgIG1lc3NhZ2UoIlByb2Nlc3NpbmcgR3JvdXAgIiwgaSwgIiBvZiAyLDgwOSwwNjIiKQojIH0KIyAKIyAKIyBHcm91cERhdGFfZGYgPC0gcmJpbmQuZmlsbChHcm91cERhdGEpCiMgc3RyKEdyb3VwRGF0YV9kZikKIyBoZWFkKEdyb3VwRGF0YV9kZikKIyB0YWlsKEdyb3VwRGF0YV9kZikKIyAjIHJtKEdyb3VwRGF0YV9kZikKIyAKIyAKIyBncm91cF8xCiMgZ3JvdXBfMV9zdGFydAojIGdyb3VwXzFfZW5kCiMgZ3JvdXBfMV9yb3dzCiMgZ3JvdXBfMl9yb3dzCiMgZ3JvdXBfM19yb3dzCiMgZ3JvdXBfNTBfcm93cwojIHN0cihncm91cF81MF9yb3dzKQojIGdyb3VwXzI4MDkwNjJfcm93cwojIEdyb3VwRGF0YVtbMV1dCiMgR3JvdXBEYXRhW1s1MF1dCiMgCiMgCiMgIyMjIyMgIFRlc3RpbmcgQXJlYSAoQmVsb3cpICAjIyMjIwojICMjIyMjICBUZXN0aW5nIEFyZWEgKEJlbG93KSAgIyMjIyMKIyAjIyMjIyAgVGVzdGluZyBBcmVhIChCZWxvdykgICMjIyMjCiMgCiMgIyBoZWFkKFN0b3BJRF9SdW5zJHN0YXJ0cywgMjApCiMgIyBoZWFkKEFsbERheXNfTmV3T3JkZXIkU3RvcF9JRCwgMjApCiMgIyAKIyAjIAojICMgZGF0IDwtIGFzLmRhdGEuZnJhbWUoYygxLDEsNyw3LDcsOSw2LDgsMiwyLDIsMSwxLDEsMSwxKSkKIyAjIGNvbG5hbWVzKGRhdClbMV0gPC0gImRhdCIKIyAjIHIgPC0gcmxlKGRhdCRkYXQpCiMgIyBkYXQkcnVuIDwtIHJlcChyJGxlbmd0aHMsIHIkbGVuZ3RocykKIyAjIGRhdCRydW5MYWcgPC0gbGFnKGRhdCRydW4pCiMgIyBkYXQkY29uZCA8LSByZXAociR2YWx1ZXMsIHIkbGVuZ3RocykKIyAjIGRhdAojICMgVmlldyhkYXQpCgpgYGAKCgpXaGVuIGNvbnNlY3V0aXZlIFN0b3BfSUQgb2NjdXJycywgb25seSB0YWtlIHRoZSBmaXJzdCBvY2N1cnJlbmNlLiBUaGlzIGlzIGRvbmUgYmVjYXVzZSB0aGUgY29tcHV0YXRpb24gdGltZSB0byBzZWxlY3Qgb25seSB0aGUgcmVjb3JkIHdpdGggdGhlIGxvbmdlc3QgRHdlbGxfVGltZSBmb3IgZWFjaCBydW4gd2FzIHRvbyBsb25nIChvdmVyIDUgZGF5cykuCgpUaGlzIGlzIHByb2JhYmx5IGxlc3MgdGhhbiBpZGVhbCB3aXRoIHJlZ2FyZHMgdG8gRHdlbGxfVGltZSwgYnV0IHNob3VsZCBub3QgbWFrZSBtdWNoIGRpZmZlcmVuY2UgZm9yIGNhbGN1bGF0aW9ucyBvZiB0cmF2ZWwgdGltZSwgc3BlZWQsIGV0Yy4KYGBge3J9CgpBbGxEYXlzX0ZpcnN0U3RvcElEIDwtIEFsbERheXNfWmlwc1tTdG9wSURfUnVucyRzdGFydHMsIF0KCmRpbShBbGxEYXlzX1ppcHMpCmRpbShBbGxEYXlzX0ZpcnN0U3RvcElEKQoKbnJvdyhBbGxEYXlzX1ppcHMpIC0gbnJvdyhBbGxEYXlzX0ZpcnN0U3RvcElEKQoKcm0oQWxsRGF5c19aaXBzLCBTdG9wSURfUnVucykKc3RyKEFsbERheXNfRmlyc3RTdG9wSUQpCgpgYGAKCgpGZWF0dXJlIGVuZ2luZWVyaW5nLgoKQ3JlYXRpbmcgbmV3IHZhcmlhYmxlcy4KYGBge3J9CgpBbGxEYXlzX0FkZFZhcnMgPC0gbXV0YXRlKEFsbERheXNfRmlyc3RTdG9wSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgT2RvbWV0ZXJfRGlzdGFuY2VfTWkgPSBPZG9tZXRlcl9EaXN0YW5jZSAvIDUyODAsICM1LDI4MCBmZWV0IGluIDEgbWlsZQogICAgICAgICAgICAgICAgICAgICAgICAgIER3ZWxsX1RpbWUyID0gYXMubnVtZXJpYyhEZXBhcnR1cmVfVGltZSAtIEV2ZW50X1RpbWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfWXIgPSBhcy5pbnRlZ2VyKHllYXIoRXZlbnRfVGltZSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfTXRoID0gYXMuaW50ZWdlcihtb250aChFdmVudF9UaW1lKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9EYXRlID0gZGF5KEV2ZW50X1RpbWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfRGF5ID0gd2RheShFdmVudF9UaW1lLCBsYWJlbCA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfSHIgPSBob3VyKEV2ZW50X1RpbWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfTWluID0gbWludXRlKEV2ZW50X1RpbWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfSHJHcm91cCA9IGZhY3RvcihpZmVsc2UoRXZlbnRfVGltZV9IciA8IDMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXAwXzIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoRXZlbnRfVGltZV9IciA8IDYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXAzXzUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoRXZlbnRfVGltZV9IciA8IDksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXA2XzgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoRXZlbnRfVGltZV9IciA8IDEyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdyb3VwOV8xMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShFdmVudF9UaW1lX0hyIDwgMTUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXAxMl8xNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShFdmVudF9UaW1lX0hyIDwgMTgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXAxNV8xNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShFdmVudF9UaW1lX0hyIDwgMjEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXAxOF8yMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShFdmVudF9UaW1lX0hyIDwgMjQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXAyMV8yMyIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSkpKSkpKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIkdyb3VwMF8yIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXAzXzUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHcm91cDZfOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdyb3VwOV8xMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdyb3VwMTJfMTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHcm91cDE1XzE3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXAxOF8yMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdyb3VwMjFfMjMiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICkKCnJtKEFsbERheXNfRmlyc3RTdG9wSUQpCnN0cihBbGxEYXlzX0FkZFZhcnMpCgpgYGAKCgpGdW5jdGlvbiBmb3IgY2FsY3VsYXRpbmcgdGhlIGRpc3RhbmNlIHRyYXZlbGVkIGJhc2VkIG9uIHRoZSBIYXZlcnNpbmUgZm9ybXVsYS4gIE9yaWdpbmFsIGNvZGUgZnJvbTogaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vZ3JlYXQtY2lyY2xlLWRpc3RhbmNlLWNhbGN1bGF0aW9ucy1pbi1yLwpgYGB7cn0KCiMgQ2FsY3VsYXRlcyB0aGUgZ2VvZGVzaWMgZGlzdGFuY2UgYmV0d2VlbiB0d28gcG9pbnRzIHNwZWNpZmllZCBieSByYWRpYW4gbGF0aXR1ZGUvbG9uZ2l0dWRlIHVzaW5nIHRoZSBIYXZlcnNpbmUgZm9ybXVsYSAoaGYpCiMgZ2NkLmhmIDwtIGZ1bmN0aW9uKGxvbmcxLCBsYXQxLCBsb25nMiwgbGF0MikgewojICAgUiA8LSA2MzcxICMgRWFydGggbWVhbiByYWRpdXMgW2ttXQojICAgZGVsdGEubG9uZyA8LSAobG9uZzIgLSBsb25nMSkKIyAgIGRlbHRhLmxhdCA8LSAobGF0MiAtIGxhdDEpCiMgICBhIDwtIHNpbihkZWx0YS5sYXQvMileMiArIGNvcyhsYXQxKSAqIGNvcyhsYXQyKSAqIHNpbihkZWx0YS5sb25nLzIpXjIKIyAgIGMgPC0gMiAqIGFzaW4obWluKDEsc3FydChhKSkpCiMgICBkID0gUiAqIGMgKiAwLjYyMTM3MSAjIDEga20gPSAwLjYyMTM3MSBtaWxlcwojICAgcmV0dXJuKGQpICMgRGlzdGFuY2UgaW4gbWlsZXMKIyB9CgpgYGAKCgpGZWF0dXJlIGVuZ2luZWVyaW5nLgoKQ3JlYXRpbmcgbW9yZSB2YXJpYWJsZXMuIENyZWF0aW5nIGEgQnVzRXZlbnQgcm93IG51bWJlciBmb3IgZnV0dXJlIGlkZW50aWZpY2F0aW9uIHB1cnBvc2VzLiBUaGVuLCBjcmVhdGluZyB2YXJpb3VzIHZhcmlhYmxlcyB0byBhbmFseXplIGRpc3RhbmNlIHRyYXZlbGVkIGFuZCBzcGVlZC4KYGBge3J9CgpBbGxEYXlzX0J1c0RheSA8LSBncm91cF9ieShBbGxEYXlzX0FkZFZhcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIEJ1c19JRCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9EYXRlCiAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKEJ1c0RheV9FdmVudE51bSA9IHJvd19udW1iZXIoKSwgICMgdXNlZCB0byBpZGVudGlmeSBCdXMgbW92ZW1lbnRzIG9uIGEgcGFydGljdWxhciBkYXRlCiAgICAgICAgIAogICAgICAgICBSb3V0ZV9MYWcxID0gbGFnKFJvdXRlKSwgICMgdXNlZCBpbiBmdXR1cmUgYW5hbHlzZXMgdG8gaWRlbnRpZnkgUm91dGUgY2hhbmdlcwogICAgICAgICBSb3V0ZUFsdF9MYWcxID0gbGFnKFJvdXRlQWx0KSwgICMgdXNlZCBpbiBmdXR1cmUgYW5hbHlzZXMgdG8gaWRlbnRpZnkgUm91dGVBbHQgKGRpcmVjdGlvbikgY2hhbmdlcwogICAgICAgICAKICAgICAgICAgT2RvbWV0ZXJfRGlzdGFuY2VfTGFnMSA9IGxhZyhPZG9tZXRlcl9EaXN0YW5jZSksCiAgICAgICAgIAogICAgICAgICBMYXRpdHVkZV9MMSA9IGxhZyhMYXRpdHVkZSksCiAgICAgICAgIExvbmdpdHVkZV9MMSA9IGxhZyhMb25naXR1ZGUpLAogICAgICAgICAjIExhdF9SYWRpYW4gPSBMYXRpdHVkZSpwaS8xODAsCiAgICAgICAgICMgTG9uZ19SYWRpYW4gPSBMb25naXR1ZGUqcGkvMTgwLAogICAgICAgICAjIExhdF9SYWRpYW5fTDEgPSBsYWcoTGF0X1JhZGlhbiksCiAgICAgICAgICMgTG9uZ19SYWRpYW5fTDEgPSBsYWcoTG9uZ19SYWRpYW4pLAogICAgICAgICAKICAgICAgICAgIyBhY2NvdW50aW5nIGZvciBwb3RlbnRpYWwgbmVnYXRpdmUgZGlzdGFuY2VzCiAgICAgICAgIFRyYXZlbERpc3RhbmNlX0Z0ID0gaWZlbHNlKE9kb21ldGVyX0Rpc3RhbmNlID4gT2RvbWV0ZXJfRGlzdGFuY2VfTGFnMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgT2RvbWV0ZXJfRGlzdGFuY2UgLSBPZG9tZXRlcl9EaXN0YW5jZV9MYWcxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pID0gVHJhdmVsRGlzdGFuY2VfRnQgLyA1MjgwLCAjNSwyODAgZmVldCBpbiAxIG1pbGUKICAgICAgICAgCiAgICAgICAgICMgVHJhdmVsRGlzdGFuY2VfTWkyID0gZ2NkLmhmKGxvbmcxID0gTG9uZ19SYWRpYW5fTDEsCiAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhdDEgPSBMYXRfUmFkaWFuX0wxLAogICAgICAgICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb25nMiA9IExvbmdfUmFkaWFuLAogICAgICAgICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXQyID0gTGF0X1JhZGlhbgogICAgICAgICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIAogICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzID0gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaWZlbHNlKChpcy5uYShMb25naXR1ZGVfTDEpIHwgaXMubmEoTGF0aXR1ZGVfTDEpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgICAgICAgIE5BLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXN0SGF2ZXJzaW5lKGNiaW5kKExvbmdpdHVkZV9MMSwgTGF0aXR1ZGVfTDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNiaW5kKExvbmdpdHVkZSwgTGF0aXR1ZGUpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICogMC4wMDA2MjEzNzEsICMgMC4wMDA2MjEzNzEgbWlsZXMgPSAxIG1ldGVyCiAgICAgICAgIAogICAgICAgICAjIGFjY291bnRpbmcgZm9yIHBvdGVudGlhbCBuZWdhdGl2ZSB0aW1lcwogICAgICAgICBUcmF2ZWxUaW1lX1NlYyA9IGFzLm51bWVyaWMoaWZlbHNlKEV2ZW50X1RpbWUgPiBsYWcoRGVwYXJ0dXJlX1RpbWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWUgLSBsYWcoRGVwYXJ0dXJlX1RpbWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRyYXZlbFRpbWVfSHIgPSBUcmF2ZWxUaW1lX1NlYyAvIDM2MDAsICMgMyw2MDAgc2Vjb25kcyBpbiAxIGhvdXIKICAgICAgICAgCiAgICAgICAgICMgYWNjb3VudGluZyBmb3IgcG90ZW50aWFsIG5lZ2F0aXZlIG9yIHplcm8gdHJhdmVsIHRpbWVzCiAgICAgICAgIFNwZWVkQXZnX01waCA9IGlmZWxzZShUcmF2ZWxUaW1lX0hyID4gMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pIC8gVHJhdmVsVGltZV9IciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIAogICAgICAgICBTdGFydF9JRCA9IGxhZyhTdG9wSURfQ2xlYW4pLAogICAgICAgICBTdGFydF9EZXNjID0gbGFnKFN0b3BfRGVzYyksCiAgICAgICAgIFN0YXJ0U3RvcF9JRCA9IGlmZWxzZShpcy5uYShTdGFydF9JRCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgiTlVMTCIsIFN0b3BJRF9DbGVhbiwgc2VwID0gIi0tIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZShTdGFydF9JRCwgU3RvcElEX0NsZWFuLCBzZXAgPSAiLS0iKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKSAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpCgoKcm0oQWxsRGF5c19BZGRWYXJzKQpzdHIoQWxsRGF5c19CdXNEYXkpCgojIHN1bW1hcnkoQWxsRGF5c19CdXNEYXkpCgojIFZpZXcodGFpbChBbGxEYXlzX0J1c0RheSwgNTApKQoKYGBgCgoKSW5zcGVjdGluZyBmb3IgaXNzdWVzIHdpdGggU3RhcnRTdG9wX0lEICh3aGVyZSB0aGUgdmFsdWUgaXMgZWl0aGVyIE5BIG9yIGNvbnRhaW5zIE5VTEwpLiBUaGV5IE9OTFkgZXhpc3Qgd2hlbiBCdXNEYXlfRXZlbnROdW0gPSAxICh3aGljaCBpcyBieSBkZXNpZ24pLiBTbyBldmVyeXRoaW5nIGxvb2tzIE9LLgpgYGB7cn0KClZpZXcoZ3JvdXBfYnkoQWxsRGF5c19CdXNEYXksCiAgICAgICAgICAgICAgU3RhcnRTdG9wX0lECiAgICAgICAgICAgICApICU+JSAKICAgICAgIHN1bW1hcmlzZSgKICAgICAgICAgQ250ID0gbigpCiAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoZGVzYyhDbnQpCiAgICAgICAgICAgICAgKQogICAgKQoKVmlldyhmaWx0ZXIoQWxsRGF5c19CdXNEYXksCiAgICAgICAgICAgIChpcy5uYShTdGFydFN0b3BfSUQpIHwKICAgICAgICAgICAgICBzdHJfZGV0ZWN0KFN0YXJ0U3RvcF9JRCwgIk5VTEwiKQogICAgICAgICAgICApICYKICAgICAgICAgICAgICBCdXNEYXlfRXZlbnROdW0gIT0gMQogICAgICAgICAgICkKICAgICkKCmBgYAoKClN0YXRzIChxdWFudGlsZXMpIG92ZXJhbGwgZm9yIFRyYXZlbERpc3RhbmNlX01pLgpgYGB7cn0KClF1YW50aWxlc19kdCA8LSBBbGxEYXlzX0J1c0RheSAlPiUgCiAgbXV0YXRlKFREX01pX3EyID0gcXVhbnRpbGUoeCA9IFRyYXZlbERpc3RhbmNlX01pLCBwcm9icyA9IDAuMDIsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFREX01pX3E5OCA9IHF1YW50aWxlKHggPSBUcmF2ZWxEaXN0YW5jZV9NaSwgcHJvYnMgPSAwLjk4LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9TZWNfcTIgPSBxdWFudGlsZSh4ID0gVHJhdmVsVGltZV9TZWMsIHByb2JzID0gMC4wMiwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVFRfU2VjX3E5OCA9IHF1YW50aWxlKHggPSBUcmF2ZWxUaW1lX1NlYywgcHJvYnMgPSAwLjk4LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9Icl9xMiA9IHF1YW50aWxlKHggPSBUcmF2ZWxUaW1lX0hyLCBwcm9icyA9IDAuMDIsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX0hyX3E5OCA9IHF1YW50aWxlKHggPSBUcmF2ZWxUaW1lX0hyLCBwcm9icyA9IDAuOTgsIG5hLnJtID0gVFJVRSkKICAgICAgICApICU+JSAKICBkYXRhLnRhYmxlKCkKCgpTdGF0cyA8LSBRdWFudGlsZXNfZHQgJT4lIAogIG11dGF0ZShURF9NaV9NZWFuID0gbWVhbihUcmF2ZWxEaXN0YW5jZV9NaSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVERfTWlfTWVhbl9GID0gbWVhbihUcmF2ZWxEaXN0YW5jZV9NaVtURF9NaV9xMiA8PSBUcmF2ZWxEaXN0YW5jZV9NaSAmIFRyYXZlbERpc3RhbmNlX01pIDw9IFREX01pX3E5OF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBURF9NaV9NZWQgPSBtZWRpYW4oVHJhdmVsRGlzdGFuY2VfTWksIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFREX01pX01lZF9GID0gbWVkaWFuKFRyYXZlbERpc3RhbmNlX01pW1REX01pX3EyIDw9IFRyYXZlbERpc3RhbmNlX01pICYgVHJhdmVsRGlzdGFuY2VfTWkgPD0gVERfTWlfcTk4XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgVERfTWlfQ250ID0gc3VtKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkKICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgVERfTWlfQ250X0YgPSBzdW0oIWlzLm5hKFRyYXZlbERpc3RhbmNlX01pW1REX01pX3EyIDw9IFRyYXZlbERpc3RhbmNlX01pICYgVHJhdmVsRGlzdGFuY2VfTWkgPD0gVERfTWlfcTk4XQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgCiAgICAgICAgIFRUX1NlY19NZWFuID0gbWVhbihUcmF2ZWxUaW1lX1NlYywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVFRfU2VjX01lYW5fRiA9IG1lYW4oVHJhdmVsVGltZV9TZWNbVFRfU2VjX3EyIDw9IFRyYXZlbFRpbWVfU2VjICYgVHJhdmVsVGltZV9TZWMgPD0gVFRfU2VjX3E5OF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX1NlY19NZWQgPSBtZWRpYW4oVHJhdmVsVGltZV9TZWMsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX1NlY19NZWRfRiA9IG1lZGlhbihUcmF2ZWxUaW1lX1NlY1tUVF9TZWNfcTIgPD0gVHJhdmVsVGltZV9TZWMgJiBUcmF2ZWxUaW1lX1NlYyA8PSBUVF9TZWNfcTk4XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBUVF9TZWNfQ250ID0gc3VtKCFpcy5uYShUcmF2ZWxUaW1lX1NlYykKICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX1NlY19DbnRfRiA9IHN1bSghaXMubmEoVHJhdmVsVGltZV9TZWNbVFRfU2VjX3EyIDw9IFRyYXZlbFRpbWVfU2VjICYgVHJhdmVsVGltZV9TZWMgPD0gVFRfU2VjX3E5OF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICksCgogICAgICAgICBUVF9Icl9NZWFuID0gbWVhbihUcmF2ZWxUaW1lX0hyLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9Icl9NZWFuX0YgPSBtZWFuKFRyYXZlbFRpbWVfSHJbVFRfSHJfcTIgPD0gVHJhdmVsVGltZV9IciAmIFRyYXZlbFRpbWVfSHIgPD0gVFRfSHJfcTk4XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX0hyX01lZCA9IG1lZGlhbihUcmF2ZWxUaW1lX0hyLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9Icl9NZWRfRiA9IG1lZGlhbihUcmF2ZWxUaW1lX0hyW1RUX0hyX3EyIDw9IFRyYXZlbFRpbWVfSHIgJiBUcmF2ZWxUaW1lX0hyIDw9IFRUX0hyX3E5OF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX0hyX0NudCA9IHN1bSghaXMubmEoVHJhdmVsVGltZV9IcikKICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgVFRfSHJfQ250X0YgPSBzdW0oIWlzLm5hKFRyYXZlbFRpbWVfSHJbVFRfSHJfcTIgPD0gVHJhdmVsVGltZV9IciAmIFRyYXZlbFRpbWVfSHIgPD0gVFRfSHJfcTk4XQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICkgJT4lIAogIGRhdGEuZnJhbWUoKQoKcm0oQWxsRGF5c19CdXNEYXkpCnJtKFF1YW50aWxlc19kdCkKc3RyKFN0YXRzKQojIFZpZXcoaGVhZChTdGF0cywgNTApKQoKYGBgCgoKU3RhdHMgZm9yIFN0YXJ0U3RvcF9JRC4KYGBge3J9CgpRdWFudGlsZXNfU1NfZHQgPC0gZ3JvdXBfYnkoU3RhdHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGFydFN0b3BfSUQKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFREX01pX1NTX3E1ID0gcXVhbnRpbGUoeCA9IFRyYXZlbERpc3RhbmNlX01pLCBwcm9icyA9IDAuMDUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFREX01pX1NTX3E5NSA9IHF1YW50aWxlKHggPSBUcmF2ZWxEaXN0YW5jZV9NaSwgcHJvYnMgPSAwLjk1LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9TZWNfU1NfcTUgPSBxdWFudGlsZSh4ID0gVHJhdmVsVGltZV9TZWMsIHByb2JzID0gMC4wNSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVFRfU2VjX1NTX3E5NSA9IHF1YW50aWxlKHggPSBUcmF2ZWxUaW1lX1NlYywgcHJvYnMgPSAwLjk1LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9Icl9TU19xNSA9IHF1YW50aWxlKHggPSBUcmF2ZWxUaW1lX0hyLCBwcm9icyA9IDAuMDUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX0hyX1NTX3E5NSA9IHF1YW50aWxlKHggPSBUcmF2ZWxUaW1lX0hyLCBwcm9icyA9IDAuOTUsIG5hLnJtID0gVFJVRSkKICAgICAgICApICU+JSAKICBkYXRhLnRhYmxlKCkKCgpTdGF0c19TdFN0IDwtIGdyb3VwX2J5KFF1YW50aWxlc19TU19kdCwKICAgICAgICAgICAgICAgICAgICAgICBTdGFydFN0b3BfSUQKICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIG11dGF0ZShURF9NaV9TU19NZWFuID0gbWVhbihUcmF2ZWxEaXN0YW5jZV9NaSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVERfTWlfU1NfTWVhbl9GID0gbWVhbihUcmF2ZWxEaXN0YW5jZV9NaVtURF9NaV9TU19xNSA8PSBUcmF2ZWxEaXN0YW5jZV9NaSAmIFRyYXZlbERpc3RhbmNlX01pIDw9IFREX01pX1NTX3E5NV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBURF9NaV9TU19NZWQgPSBtZWRpYW4oVHJhdmVsRGlzdGFuY2VfTWksIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFREX01pX1NTX01lZF9GID0gbWVkaWFuKFRyYXZlbERpc3RhbmNlX01pW1REX01pX1NTX3E1IDw9IFRyYXZlbERpc3RhbmNlX01pICYgVHJhdmVsRGlzdGFuY2VfTWkgPD0gVERfTWlfU1NfcTk1XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgVERfTWlfU1NfQ250ID0gc3VtKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgVERfTWlfU1NfQ250X0YgPSBzdW0oIWlzLm5hKFRyYXZlbERpc3RhbmNlX01pW1REX01pX1NTX3E1IDw9IFRyYXZlbERpc3RhbmNlX01pICYgVHJhdmVsRGlzdGFuY2VfTWkgPD0gVERfTWlfU1NfcTk1XQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgCiAgICAgICAgIFRUX1NlY19TU19NZWFuID0gbWVhbihUcmF2ZWxUaW1lX1NlYywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVFRfU2VjX1NTX01lYW5fRiA9IG1lYW4oVHJhdmVsVGltZV9TZWNbVFRfU2VjX1NTX3E1IDw9IFRyYXZlbFRpbWVfU2VjICYgVHJhdmVsVGltZV9TZWMgPD0gVFRfU2VjX1NTX3E5NV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX1NlY19TU19NZWQgPSBtZWRpYW4oVHJhdmVsVGltZV9TZWMsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX1NlY19TU19NZWRfRiA9IG1lZGlhbihUcmF2ZWxUaW1lX1NlY1tUVF9TZWNfU1NfcTUgPD0gVHJhdmVsVGltZV9TZWMgJiBUcmF2ZWxUaW1lX1NlYyA8PSBUVF9TZWNfU1NfcTk1XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBUVF9TZWNfU1NfQ250ID0gc3VtKCFpcy5uYShUcmF2ZWxUaW1lX1NlYykpLAogICAgICAgICBUVF9TZWNfU1NfQ250X0YgPSBzdW0oIWlzLm5hKFRyYXZlbFRpbWVfU2VjW1RUX1NlY19TU19xNSA8PSBUcmF2ZWxUaW1lX1NlYyAmIFRyYXZlbFRpbWVfU2VjIDw9IFRUX1NlY19TU19xOTVdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCgogICAgICAgICBUVF9Icl9TU19NZWFuID0gbWVhbihUcmF2ZWxUaW1lX0hyLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9Icl9TU19NZWFuX0YgPSBtZWFuKFRyYXZlbFRpbWVfSHJbVFRfSHJfU1NfcTUgPD0gVHJhdmVsVGltZV9IciAmIFRyYXZlbFRpbWVfSHIgPD0gVFRfSHJfU1NfcTk1XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX0hyX1NTX01lZCA9IG1lZGlhbihUcmF2ZWxUaW1lX0hyLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9Icl9TU19NZWRfRiA9IG1lZGlhbihUcmF2ZWxUaW1lX0hyW1RUX0hyX1NTX3E1IDw9IFRyYXZlbFRpbWVfSHIgJiBUcmF2ZWxUaW1lX0hyIDw9IFRUX0hyX1NTX3E5NV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX0hyX1NTX0NudCA9IHN1bSghaXMubmEoVHJhdmVsVGltZV9IcikpLAogICAgICAgICBUVF9Icl9TU19DbnRfRiA9IHN1bSghaXMubmEoVHJhdmVsVGltZV9IcltUVF9Icl9TU19xNSA8PSBUcmF2ZWxUaW1lX0hyICYgVHJhdmVsVGltZV9IciA8PSBUVF9Icl9TU19xOTVdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKSAlPiUgCiAgZGF0YS5mcmFtZSgpCgpybShTdGF0cykKcm0oUXVhbnRpbGVzX1NTX2R0KQpzdHIoU3RhdHNfU3RTdCkKIyBWaWV3KGhlYWQoU3RhdHNfU3RTdCwgNTApKQoKYGBgCgoKU3RhdHMgZm9yIFN0YXJ0U3RvcF9JRCB3aXRoIEV2ZW50X1RpbWVfSHJHcm91cC4KYGBge3J9CgpRdWFudGlsZXNfU1NIR19kdCA8LSBncm91cF9ieShTdGF0c19TdFN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGFydFN0b3BfSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfSHJHcm91cAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIG11dGF0ZShURF9NaV9TU0hHX3E1ID0gcXVhbnRpbGUoeCA9IFRyYXZlbERpc3RhbmNlX01pLCBwcm9icyA9IDAuMDUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFREX01pX1NTSEdfcTk1ID0gcXVhbnRpbGUoeCA9IFRyYXZlbERpc3RhbmNlX01pLCBwcm9icyA9IDAuOTUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX1NlY19TU0hHX3E1ID0gcXVhbnRpbGUoeCA9IFRyYXZlbFRpbWVfU2VjLCBwcm9icyA9IDAuMDUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX1NlY19TU0hHX3E5NSA9IHF1YW50aWxlKHggPSBUcmF2ZWxUaW1lX1NlYywgcHJvYnMgPSAwLjk1LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9Icl9TU0hHX3E1ID0gcXVhbnRpbGUoeCA9IFRyYXZlbFRpbWVfSHIsIHByb2JzID0gMC4wNSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVFRfSHJfU1NIR19xOTUgPSBxdWFudGlsZSh4ID0gVHJhdmVsVGltZV9IciwgcHJvYnMgPSAwLjk1LCBuYS5ybSA9IFRSVUUpCiAgICAgICAgKSAlPiUgCiAgZGF0YS50YWJsZSgpCgoKU3RhdHNfU3RTdF9IckdycCA8LSBncm91cF9ieShRdWFudGlsZXNfU1NIR19kdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGFydFN0b3BfSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9Ickdyb3VwCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBtdXRhdGUoVERfTWlfU1NIR19NZWFuID0gbWVhbihUcmF2ZWxEaXN0YW5jZV9NaSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVERfTWlfU1NIR19NZWFuX0YgPSBtZWFuKFRyYXZlbERpc3RhbmNlX01pW1REX01pX1NTSEdfcTUgPD0gVHJhdmVsRGlzdGFuY2VfTWkgJiBUcmF2ZWxEaXN0YW5jZV9NaSA8PSBURF9NaV9TU0hHX3E5NV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgVERfTWlfU1NIR19NZWQgPSBtZWRpYW4oVHJhdmVsRGlzdGFuY2VfTWksIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFREX01pX1NTSEdfTWVkX0YgPSBtZWRpYW4oVHJhdmVsRGlzdGFuY2VfTWlbVERfTWlfU1NIR19xNSA8PSBUcmF2ZWxEaXN0YW5jZV9NaSAmIFRyYXZlbERpc3RhbmNlX01pIDw9IFREX01pX1NTSEdfcTk1XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFREX01pX1NTSEdfQ250ID0gc3VtKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBURF9NaV9TU0hHX0NudF9GID0gc3VtKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaVtURF9NaV9TU0hHX3E1IDw9IFRyYXZlbERpc3RhbmNlX01pICYgVHJhdmVsRGlzdGFuY2VfTWkgPD0gVERfTWlfU1NIR19xOTVdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgCiAgICAgICAgIFRUX1NlY19TU0hHX01lYW4gPSBtZWFuKFRyYXZlbFRpbWVfU2VjLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9TZWNfU1NIR19NZWFuX0YgPSBtZWFuKFRyYXZlbFRpbWVfU2VjW1RUX1NlY19TU0hHX3E1IDw9IFRyYXZlbFRpbWVfU2VjICYgVHJhdmVsVGltZV9TZWMgPD0gVFRfU2VjX1NTSEdfcTk1XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX1NlY19TU0hHX01lZCA9IG1lZGlhbihUcmF2ZWxUaW1lX1NlYywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVFRfU2VjX1NTSEdfTWVkX0YgPSBtZWRpYW4oVHJhdmVsVGltZV9TZWNbVFRfU2VjX1NTSEdfcTUgPD0gVHJhdmVsVGltZV9TZWMgJiBUcmF2ZWxUaW1lX1NlYyA8PSBUVF9TZWNfU1NIR19xOTVdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBUVF9TZWNfU1NIR19DbnQgPSBzdW0oIWlzLm5hKFRyYXZlbFRpbWVfU2VjKSksCiAgICAgICAgIFRUX1NlY19TU0hHX0NudF9GID0gc3VtKCFpcy5uYShUcmF2ZWxUaW1lX1NlY1tUVF9TZWNfU1NIR19xNSA8PSBUcmF2ZWxUaW1lX1NlYyAmIFRyYXZlbFRpbWVfU2VjIDw9IFRUX1NlY19TU0hHX3E5NV0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCgogICAgICAgICBUVF9Icl9TU0hHX01lYW4gPSBtZWFuKFRyYXZlbFRpbWVfSHIsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX0hyX1NTSEdfTWVhbl9GID0gbWVhbihUcmF2ZWxUaW1lX0hyW1RUX0hyX1NTSEdfcTUgPD0gVHJhdmVsVGltZV9IciAmIFRyYXZlbFRpbWVfSHIgPD0gVFRfSHJfU1NIR19xOTVdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX0hyX1NTSEdfTWVkID0gbWVkaWFuKFRyYXZlbFRpbWVfSHIsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX0hyX1NTSEdfTWVkX0YgPSBtZWRpYW4oVHJhdmVsVGltZV9IcltUVF9Icl9TU0hHX3E1IDw9IFRyYXZlbFRpbWVfSHIgJiBUcmF2ZWxUaW1lX0hyIDw9IFRUX0hyX1NTSEdfcTk1XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX0hyX1NTSEdfQ250ID0gc3VtKCFpcy5uYShUcmF2ZWxUaW1lX0hyKSksCiAgICAgICAgIFRUX0hyX1NTSEdfQ250X0YgPSBzdW0oIWlzLm5hKFRyYXZlbFRpbWVfSHJbVFRfSHJfU1NIR19xNSA8PSBUcmF2ZWxUaW1lX0hyICYgVHJhdmVsVGltZV9IciA8PSBUVF9Icl9TU0hHX3E5NV0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKSAlPiUgCiAgZGF0YS5mcmFtZSgpCgpybShTdGF0c19TdFN0KQpybShRdWFudGlsZXNfU1NIR19kdCkKc3RyKFN0YXRzX1N0U3RfSHJHcnApCiMgVmlldyhoZWFkKFN0YXRzX1N0U3RfSHJHcnAsIDUwKSkKCmBgYAoKCkZlYXR1cmUgZW5naW5lZXJpbmcuCgpDcmVhdGluZyBhIEJ1c0V2ZW50Um91dGUgcm93IG51bWJlciwgYW5kIGEgUm91dGVBbHRfTGFnMSBpbmRpY2F0b3IgZm9yIGZ1dHVyZSBpZGVudGlmaWNhdGlvbiBwdXJwb3Nlcy4gCmBgYHtyfQoKIyBybShRdWFudGlsZXNfZHQpCiMgcm0oUXVhbnRpbGVzX1NTX2R0KQojIHJtKEFsbERheXNfQnVzRGF5KQojIHJtKFF1YW50aWxlc19TU0hHX2R0KQojIHJtKFN0YXRzX1N0U3QpCgojIEFsbERheXNfQnVzRGF5Um91dGUgPC0gZ3JvdXBfYnkoU3RhdHNfU3RTdF9IckdycCwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEJ1c19JRCwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfRGF0ZSwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJvdXRlCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAojICAgbXV0YXRlKFJvdXRlQWx0X0xhZzIgPSBsYWcoUm91dGVBbHQpICAjIHVzZWQgaW4gZnV0dXJlIGFuYWx5c2VzIHRvIGlkZW50aWZ5IFJvdXRlQWx0IChkaXJlY3Rpb24pIGNoYW5nZXMKIyAgICAgICAgICAKIyAgICAgICAgICAjIE9kb21ldGVyX0Rpc3RhbmNlX0xhZzEgPSBsYWcoT2RvbWV0ZXJfRGlzdGFuY2UpLAojICAgICAgICAgICMgCiMgICAgICAgICAgIyAjIGFjY291bnRpbmcgZm9yIHBvdGVudGlhbCBuZWdhdGl2ZSBkaXN0YW5jZXMKIyAgICAgICAgICAjIFRyYXZlbERpc3RhbmNlX0Z0ID0gaWZlbHNlKE9kb21ldGVyX0Rpc3RhbmNlID49IE9kb21ldGVyX0Rpc3RhbmNlX0xhZzEsCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICBPZG9tZXRlcl9EaXN0YW5jZSAtIE9kb21ldGVyX0Rpc3RhbmNlX0xhZzEsCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQQojICAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICApLAojICAgICAgICAgICMgVHJhdmVsRGlzdGFuY2VfTWkgPSBUcmF2ZWxEaXN0YW5jZV9GdCAvIDUyODAsICM1LDI4MCBmZWV0IGluIDEgbWlsZQojICAgICAgICAgICMgCiMgICAgICAgICAgIyAjIGFjY291bnRpbmcgZm9yIHBvdGVudGlhbCBuZWdhdGl2ZSB0aW1lcwojICAgICAgICAgICMgVHJhdmVsVGltZV9TZWMgPSBhcy5udW1lcmljKGlmZWxzZShFdmVudF9UaW1lID49IGxhZyhEZXBhcnR1cmVfVGltZSksCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWUgLSBsYWcoRGVwYXJ0dXJlX1RpbWUpLAojICAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQQojICAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKIyAgICAgICAgICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiMgICAgICAgICAgIyBUcmF2ZWxUaW1lX0hyID0gVHJhdmVsVGltZV9TZWMgLyAzNjAwLCAjIDMsNjAwIHNlY29uZHMgaW4gMSBob3VyCiMgICAgICAgICAgIyAKIyAgICAgICAgICAjICMgYWNjb3VudGluZyBmb3IgcG90ZW50aWFsIG5lZ2F0aXZlIG9yIHplcm8gdHJhdmVsIHRpbWVzCiMgICAgICAgICAgIyBTcGVlZEF2Z19NcGggPSBpZmVsc2UoVHJhdmVsVGltZV9IciA+IDAsCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWkgLyBUcmF2ZWxUaW1lX0hyLAojICAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgIE5BCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICApCiMgICAgICAgICApICU+JSAKIyAgIGRhdGEuZnJhbWUoKQojIAojIHJtKFN0YXRzX1N0U3RfSHJHcnApCiMgc3RyKEFsbERheXNfQnVzRGF5Um91dGUpCgpgYGAKCgpGZWF0dXJlIGVuZ2luZWVyaW5nLgoKQ2FsY3VsYXRpbmcgYSB2YXJpYWJsZSB0byBrbm93IGlmIHRoZSBSb3V0ZUFsdCBjaGFuZ2VkLiBDb3VsZCBiZSB1c2VmdWwgaW4gaGVscGluZyBpZGVudGlmeWluZyB3ZWlyZG5lc3MgaW4gY2FsY3VsYXRlZCBkaXN0YW5jZXMgYW5kIHNwZWVkcy4KYGBge3J9CgojIHJtKFN0YXRzX1N0U3RfSHJHcnApCgpBbGxEYXlzX0RpckNoYW5nZSA8LSBTdGF0c19TdFN0X0hyR3JwICU+JSAgIyBBbGxEYXlzX0J1c0RheVJvdXRlICU+JSAKICBtdXRhdGUoUnRlQ2hhbmdlID0gaWZlbHNlKFJvdXRlID09IFJvdXRlX0xhZzEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU2FtZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2hhbmdlIgogICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBSdGVDaGFuZ2UyID0gZmFjdG9yKGlmZWxzZShpcy5uYShSdGVDaGFuZ2UpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2hhbmdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUnRlQ2hhbmdlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgRGlyQ2hhbmdlID0gaWZlbHNlKFJvdXRlQWx0ID09IFJvdXRlQWx0X0xhZzEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU2FtZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2hhbmdlIgogICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBEaXJDaGFuZ2UyID0gZmFjdG9yKGlmZWxzZShpcy5uYShEaXJDaGFuZ2UpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2hhbmdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGlyQ2hhbmdlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICkKCiMgcm0oQWxsRGF5c19CdXNEYXlSb3V0ZSkKcm0oU3RhdHNfU3RTdF9IckdycCkKc3RyKEFsbERheXNfRGlyQ2hhbmdlKQoKVmlldyhmaWx0ZXIoQWxsRGF5c19EaXJDaGFuZ2UsCiAgICAgICAgICAgIGJldHdlZW4oUm93TnVtX09HLCAyNTcwMDYwLCAyNTcwMDgwKQogICAgICAgICAgICkgJT4lIAogICAgICAgc2VsZWN0KC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICAgICkKICAgICkKCmBgYAoKClJlLW9yZGVyaW5nIHRoZSB2YXJpYWJsZXMgdG8gZWFzZSB3aXRoIGNvbXByZWhlbnNpb24uCmBgYHtyfQoKQWxsRGF5c19OZXdPcmRlciA8LSAgc2VsZWN0KEFsbERheXNfRGlyQ2hhbmdlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgUm93TnVtX09HLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVW5pcXVlTGF0TG5nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGFydFN0b3BfSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBCdXNEYXlfRXZlbnROdW0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBCdXNfSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBSb3V0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJ0ZUNoYW5nZTIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBSb3V0ZUFsdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgUm91dGVBbHRfTGFnMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIERpckNoYW5nZTIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBSb3V0ZV9EaXJlY3Rpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdG9wX1NlcXVlbmNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RhcnRfSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGFydF9EZXNjLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBTdG9wX0lELAogICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RvcElEX0NsZWFuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RvcElEX0luZGljYXRvciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0b3BfRGVzYywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvdW50cnlDb2RlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RvcF9TdGF0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0b3BfQ291bnR5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RvcF9DaXR5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RvcF9aaXAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UeXBlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfRGVzY3JpcHRpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX1lyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9NdGgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0RhdGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0RheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfSHIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0hyR3JvdXAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX01pbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBEZXBhcnR1cmVfVGltZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIER3ZWxsX1RpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBEd2VsbF9UaW1lMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIERlbHRhX1RpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBMYXRpdHVkZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIExvbmdpdHVkZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIEhlYWRpbmcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBPZG9tZXRlcl9EaXN0YW5jZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIE9kb21ldGVyX0Rpc3RhbmNlX0xhZzEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBPZG9tZXRlcl9EaXN0YW5jZV9NaSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX0Z0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfcTIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9xOTgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU19xNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTX3E5NSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTSEdfcTUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU0hHX3E5NSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX01lYW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9NZWFuX0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU19NZWFuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NfTWVhbl9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NIR19NZWFuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NIR19NZWFuX0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9NZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9NZWRfRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTX01lZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTX01lZF9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NIR19NZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU0hHX01lZF9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfQ250LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfQ250X0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU19DbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU19DbnRfRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTSEdfQ250LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NIR19DbnRfRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX3EyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX3E5OCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19xNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19xOTUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NIR19xNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU0hHX3E5NSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19NZWFuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX01lYW5fRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19NZWFuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTX01lYW5fRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU0hHX01lYW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NIR19NZWFuX0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfTWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX01lZF9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTX01lZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19NZWRfRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU0hHX01lZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU0hHX01lZF9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX0NudCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19DbnRfRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19DbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NfQ250X0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NIR19DbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NIR19DbnRfRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfSHIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9xMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX3E5OCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX1NTX3E1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfSHJfU1NfcTk1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfSHJfU1NIR19xNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX1NTSEdfcTk1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfSHJfTWVhbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX01lYW5fRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX1NTX01lYW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9TU19NZWFuX0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9TU0hHX01lYW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9TU0hHX01lYW5fRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX01lZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX01lZF9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfSHJfU1NfTWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfSHJfU1NfTWVkX0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9TU0hHX01lZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX1NTSEdfTWVkX0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9DbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9DbnRfRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX1NTX0NudCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX1NTX0NudF9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfSHJfU1NIR19DbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9TU0hHX0NudF9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgU3BlZWRBdmdfTXBoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCnJtKEFsbERheXNfRGlyQ2hhbmdlKQpzdHIoc2VsZWN0KEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgLW1hdGNoZXMoIihxKDJ8NXwoOTUpfCg5OCkpKXxNZWFufE1lZHxDbnQiKQogICAgICAgICAgKQogICApCnN0cihBbGxEYXlzX05ld09yZGVyKQoKIyBWaWV3KGhlYWQoQWxsRGF5c19OZXdPcmRlciwgNTAwKSkKIyBWaWV3KHRhaWwoQWxsRGF5c19OZXdPcmRlciwgNTAwKSkKCmBgYAoKClN1bW1hcml6aW5nIHRoZSBkYXRhIHRvIGhlbHAgc3BvdCBhbm9tb2xpZXMuCmBgYHtyfQoKVmlldyhncm91cF9ieShBbGxEYXlzX05ld09yZGVyLAogICAgICAgICAgICAgIFN0b3BfQ2l0eSkgJT4lIAogICAgICAgc3VtbWFyaXNlKENudF9OdW0gPSBuKCksCiAgICAgICAgICAgICAgICAgQ250X1BjdCA9IDEwMCpDbnRfTnVtIC8gKG5yb3coQWxsRGF5c19OZXdPcmRlcikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoZGVzYyhDbnRfTnVtKSkKKQoKc3VtbWFyeShBbGxEYXlzX05ld09yZGVyKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaS4KClZpZXcoVHJhdkRpc3RNaV9QY3RpbGVzKTogOTklIG9mIFRyYXZlbERpc3RhbmNlX01pIGFyZSBhYm91dCAxIG1pbGUgb3IgbGVzcy4uLmJ1dCBzb21lIHdlaXJkIFRyYXZlbERpc3RhbmNlX01pIHZhbHVlcyAoZS5nLiwgNTg0IG1pbGVzIHRyYXZlbGVkKSBleGlzdC4KYGBge3J9CgpUcmF2RGlzdE1pX050aWxlIDwtIGFzLmRhdGEuZnJhbWUoQWxsRGF5c19OZXdPcmRlciRUcmF2ZWxEaXN0YW5jZV9NaSkgJT4lIAogIG11dGF0ZSgjUGN0aWxlID0gbnRpbGUoQWxsRGF5c19OZXdPcmRlciRUcmF2ZWxEaXN0YW5jZV9NaSwgMTAwKSwKICAgICAgICAgI01pblIgPSBtaW5fcmFuayhBbGxEYXlzX05ld09yZGVyJFRyYXZlbERpc3RhbmNlX01pKSwKICAgICAgICAgUGN0UiA9IHBlcmNlbnRfcmFuayhBbGxEYXlzX05ld09yZGVyJFRyYXZlbERpc3RhbmNlX01pKSwKICAgICAgICAgUGN0Ul9Sb3VuZCA9IHJvdW5kKFBjdFIsIDIpCiAgICAgICAgKSAKCmNvbG5hbWVzKFRyYXZEaXN0TWlfTnRpbGUpWzFdIDwtICJUcmF2ZWxEaXN0YW5jZV9NaSIKIyBzdHIoVHJhdkRpc3RNaV9OdGlsZSkKClRyYXZEaXN0TWlfTnRpbGVfUm93cyA8LSBucm93KFRyYXZEaXN0TWlfTnRpbGUpCgojIFZpZXcodGFpbChUcmF2RGlzdE1pX050aWxlLCA1MDApKQoKClRyYXZEaXN0TWlfUGN0aWxlcyA8LSBncm91cF9ieShUcmF2RGlzdE1pX050aWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUGN0Ul9Sb3VuZAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzdW1tYXJpc2UoCiAgICBNaW5UcmF2RGlzdE1pQXRQY3RpbGUgPSBtaW4oVHJhdmVsRGlzdGFuY2VfTWkpLAogICAgQ250c0F0UGN0aWxlID0gbigpLAogICAgUGN0c0F0UGN0aWxlID0gQ250c0F0UGN0aWxlIC8gVHJhdkRpc3RNaV9OdGlsZV9Sb3dzCiAgKSAlPiUgCiAgbXV0YXRlKEN1bVN1bVBBdFAgPSBjdW1zdW0oUGN0c0F0UGN0aWxlKQogICAgICAgICkKCnJtKFRyYXZEaXN0TWlfTnRpbGUpCnJtKFRyYXZEaXN0TWlfTnRpbGVfUm93cykKClZpZXcoVHJhdkRpc3RNaV9QY3RpbGVzKQpUcmF2RGlzdE1pX1BjdGlsZXMKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsRGlzdGFuY2VfTWkuCgpXaHkgYXJlIHNvbWUgVHJhdmVsRGlzdGFuY2VfTWkgIk5BIj8gSXQgbG9va3MgbGlrZSBwYXJ0aWFsbHkgYmVjYXVzZSB0aGUgcmVjb3JkcyBhcmUgdGhlIGZpcnN0IHRyaXAgb2YgdGhlIGRheSAoZm9yIHRoYXQgYnVzKSwgc28gSSBwdXJwb3NlZnVsbHkgc2V0IHRoZSBkaXN0YW5jZSB0byAiTkEiLiBBbm90aGVyIHJlYXNvbiBpcyBkdWUgdG8gdGhlIG9kb21ldGVyIHJlY29yZGluZyBhIHZhbHVlIGxlc3MgdGhhbiB0aGUgcHJldmlvdXMgb2RvbWV0ZXIgcmVjb3JkaW5nLiBJbiBtb3N0IGNhc2VzLCBJIGhhdmUgbm8gZXhwbGFuYXRpb24gZm9yIHRoaXMgLSB0aG91Z2ggSSBoYXZlIG9ic2VydmVkIGFib3V0IDY3JSBvZiBhbGwgaW5zdGFuY2VzIHdoZXJlIFRyYXZlbERpc3RhbmNlX01pIGlzIE5BIChvdGhlciB0aGFuIGJlY2F1c2UgaXQncyB0aGUgZmlyc3QgcmVjb3JkIG9mIHRoZSBkYXkpIGFyZSBpbnN0YW5jZXMgd2hlcmUgRGlyQ2hhbmdlMiBpcyAiQ2hhbmdlIi4gVGhpcyBpcyB3ZWlyZCBhbmQgc2hvdWxkIGJlIGFza2VkIHRvIFdNQVRBLgpgYGB7cn0KCiMgVmlldyhoZWFkKEFsbERheXNfTmV3T3JkZXIsIDUwMCkpCgpWaWV3KGZpbHRlcihBbGxEYXlzX05ld09yZGVyLAogICAgICAgICAgICBCdXNEYXlfRXZlbnROdW0gIT0gMSAjIFdoZW4gQnVzRGF5X0V2ZW50TnVtID09IDEsIFRyYXZlbERpc3RhbmNlX01pIGlzIE5BIGJ5IGRlc2lnbiAoZG9uJ3Qgd2FudCB0byBjYWxjdWxhdGUgZGlzdGFuY2UgYmFzZWQgb24geWVzdGVyZGF5J3MgcG9zaXRpb24pCiAgICAgICAgICAgKSAlPiUgCiAgICAgICBncm91cF9ieShTdGFydFN0b3BfSUQpICU+JSAKICAgICAgIHN1bW1hcmlzZShDbnRzID0gc3VtKGlzLm5hKFRyYXZlbERpc3RhbmNlX01pKQogICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoZGVzYyhDbnRzKQogICAgICAgICAgICAgICkKICAgICkKClZpZXcoZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgIFN0YXJ0U3RvcF9JRCA9PSAiMTAwMDI0NS0tMTAwMDIxMSIKICAgICAgICAgICApICU+JSAKICAgICAgIHNlbGVjdChSb3dOdW1fT0csCiAgICAgICAgICAgICAgU3RhcnRTdG9wX0lELAogICAgICAgICAgICAgIEV2ZW50X1RpbWUsCiAgICAgICAgICAgICAgRXZlbnRfVGltZV9Ickdyb3VwLAogICAgICAgICAgICAgIEJ1c19JRCwKICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaSwKICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzLAogICAgICAgICAgICAgIFREX01pX1NTX01lYW4sCiAgICAgICAgICAgICAgVERfTWlfU1NfTWVhbl9GLAogICAgICAgICAgICAgIFREX01pX1NTSEdfTWVhbiwKICAgICAgICAgICAgICBURF9NaV9TU0hHX01lYW5fRiwKICAgICAgICAgICAgICBURF9NaV9TU19NZWQsCiAgICAgICAgICAgICAgVERfTWlfU1NfTWVkX0YsCiAgICAgICAgICAgICAgVERfTWlfU1NIR19NZWQsCiAgICAgICAgICAgICAgVERfTWlfU1NIR19NZWRfRiwKICAgICAgICAgICAgICBURF9NaV9TU19DbnQsCiAgICAgICAgICAgICAgVERfTWlfU1NfQ250X0YsCiAgICAgICAgICAgICAgVERfTWlfU1NIR19DbnQsCiAgICAgICAgICAgICAgVERfTWlfU1NIR19DbnRfRgogICAgICAgICAgICAgICkgJT4lIAogICAgICAgbXV0YXRlKFJhdGlvX01lYW5Ub0h2cnMgPSBURF9NaV9TU19NZWFuIC8gVHJhdmVsRGlzdGFuY2VfTWlfSHZycykgJT4lIAogICAgICAgYXJyYW5nZShFdmVudF9UaW1lKQogICAgKQoKVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgaXMubmEoVHJhdmVsRGlzdGFuY2VfTWkpCiAgICAgICAgICAgKQogICAgKQoKIyBUaGVzZSByZWNvcmRzIGFyZSBOQSBiZWN1YXNlIHRoZSByZWNvcmQgaXMgdGhlIGZpcnN0IHJlY29yZCBvZiB0aGUgZGF5ICh0aGUgRXZlbnRfVGltZV9EYXRlKQpWaWV3KGZpbHRlcihBbGxEYXlzX05ld09yZGVyLAogICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgMzI2LCAzNDYpIHwgIyAzMzYKICAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgNTkxLCA2MTEpIHwgIyA2MDEKICAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgODQ1LCA4NjUpICMgODU1CiAgICAgICAgICAgKQogICAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaS4KClRoZXNlIHJlY29yZHMgYXJlIE5BIGJlY3Vhc2UgdGhlIGN1cnJlbnQgcmVjb3JkIG9kb21ldGVyIGlzIGxlc3MgdGhhbiB0aGUgcHJldmlvdXMgcmVjb3JkIG9kb21ldGVyLiBUaGVvcmV0aWNhbGx5LCB0aGlzIHNob3VsZCBOT1QgaGFwcGVuLiBNZTogaXQgYXBwZWFycyB0aGF0IGFib3V0IDY3JSBvZiBhbGwgaW5zdGFuY2VzIHdoZXJlIFRyYXZlbERpc3RhbmNlX01pIGlzIE5BIChvdGhlciB0aGFuIGJlY2F1c2UgaXQncyB0aCBmaXJzdCByZWNvcmQgb2YgdGhlIGRheSkgYXJlIGluc3RhbmNlcyB3aGVyZSBEaXJDaGFuZ2UyIGlzICJDaGFuZ2UiLiBUaGlzIGlzIHdlaXJkIGFuZCBzaG91bGQgYmUgYXNrZWQgdG8gV01BVEEuCmBgYHtyfQoKVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDE5NCwgMjE0KSB8ICMgMjA0CiAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDQ0MCwgNDYwKSB8ICMgNDUwCiAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDQ3OCwgNDk4KSB8ICMgNDg4CiAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDUxMCwgNTMwKSAjIDUyMAogICAgICAgICAgICkKICAgICkKClRlc3RUYWJsZSA8LSBmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgICAgICAgICBCdXNEYXlfRXZlbnROdW0gIT0gMQogICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFRyYXZlbERpc3RhbmNlX05BID0gYXMuZmFjdG9yKGlmZWxzZShpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVHJ1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRmFsc2UiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKSAlPiUKICBncm91cF9ieShEaXJDaGFuZ2UyLCBUcmF2ZWxEaXN0YW5jZV9OQSkgJT4lCiAgc3VtbWFyaXNlKFRyYXZEaXN0TWlfTkFDbnRzID0gbigpCiAgICAgICAgICAgKQoKIyBUZXN0VGFibGUKClRlc3RUYWJsZV9TcHJlYWQgPC0gYXMuZGF0YS5mcmFtZShzcHJlYWQoVGVzdFRhYmxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX05BLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZEaXN0TWlfTkFDbnRzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHNlbGVjdChGYWxzZSwKICAgICAgICAgVHJ1ZQogICAgICAgICkKCnJvdy5uYW1lcyhUZXN0VGFibGVfU3ByZWFkKSA8LSBjKCJDaGFuZ2UiLCAiU2FtZSIpCiMgc3RyKFRlc3RUYWJsZV9TcHJlYWQpCiMgVGVzdFRhYmxlX1NwcmVhZAoKcHJvcC50YWJsZShhcy50YWJsZShhcy5tYXRyaXgoVGVzdFRhYmxlX1NwcmVhZCkKICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgMQogICAgICAgICAgKQoKcHJvcC50YWJsZShhcy50YWJsZShhcy5tYXRyaXgoVGVzdFRhYmxlX1NwcmVhZCkKICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgMgogICAgICAgICAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaS4KCkxldCdzIGxvb2sgYXQganVzdCB0aGUgVHJhdmVsRGlzdGFuY2VfTWkgdmFsdWVzIHRoYXQgYXJlIE5PVCAiTkEiLgpgYGB7cn0KCnJtKFRlc3RUYWJsZSwgVGVzdFRhYmxlX1NwcmVhZCkKClRyYXZlbERpc3RhbmNlX01pX05vTkEgPC0gZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgVHJhdmVsRGlzdGFuY2VfTWkgIT0gMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgpkaW0oQWxsRGF5c19OZXdPcmRlcikKZGltKFRyYXZlbERpc3RhbmNlX01pX05vTkEpCm5yb3coQWxsRGF5c19OZXdPcmRlcikgLSBucm93KFRyYXZlbERpc3RhbmNlX01pX05vTkEpCgpzdHIoVHJhdmVsRGlzdGFuY2VfTWlfTm9OQSkKc3VtbWFyeShUcmF2ZWxEaXN0YW5jZV9NaV9Ob05BKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaS4KCkxldCdzIHBsb3QganVzdCB0aGUgVHJhdmVsRGlzdGFuY2VfTWkgdmFsdWVzIHRoYXQgYXJlIE5PVCAiTkEiLgpgYGB7cn0KClRyYXZEaXN0TWlfSGlzdERlbiA8LSBnZ3Bsb3Qoc2VsZWN0KFRyYXZlbERpc3RhbmNlX01pX05vTkEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IFRyYXZlbERpc3RhbmNlX01pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gLi5kZW5zaXR5Li4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMDUsIGZpbGwgPSAibGlnaHRibHVlIiwgY29sb3VyID0gImdyZXk2MCIsIHNpemUgPSAwLjIpICsKICBnZW9tX2xpbmUoc3RhdCA9ICJkZW5zaXR5IiwgY29sb3VyID0gInJlZCIpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwgMS41KSwgeWxpbSA9IGMoMCwgNC4wKQogICAgICAgICAgICAgICAgICkgKwogIGxhYnModGl0bGUgPSAiVmFyaWF0aW9uIGluIERpc3RhbmNlIEJldHdlZW4gU3RvcHMiLAogICAgICAgeCA9ICJUcmF2ZWwgRGlzdGFuY2UgKG1pbGVzKSIsCiAgICAgICB5ID0gIkRlbnNpdHkiCiAgICAgICkKClRyYXZEaXN0TWlfSGlzdERlbgoKYGBgCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbERpc3RhbmNlX01pLgoKTG9va2luZyBhdCB0aGUgZXh0cmVtZWx5IGxhcmdlIFRyYXZlbERpc3RhbmNlX01pIHZhbHVlcy4gU29tZSAoYXByb3ggMjclKSBvZiBUcmF2ZWxEaXN0YW5jZV9NaSB2YWx1ZXMgPiAxIG1pbGUgYXJlIHdoZW4gdGhlIERpckNoYW5nZTIgY2hhbmdlcy4uLmJ1dCB3aGF0IGFib3V0IHRoZSBvdGhlciB+NzMlPwpgYGB7cn0KCnJtKFRyYXZlbERpc3RhbmNlX01pX05vTkEpCgojIGV4YW1wbGVzIG9mIHdlaXJkbHkgbGFyZ2UgVHJhdmVsRGlzdGFuY2VfTWkKVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWkgPiAxLjE1ODcxMjEyMTIgIyAxLjE1ODcxMjEyMTIgaXMgdGhlIDk5dGggcGVyY2VudGlsZQogICAgICAgICAgICkgJT4lIAogICAgICAgYXJyYW5nZShkZXNjKFRyYXZlbERpc3RhbmNlX01pKQogICAgICAgICAgICAgICkKICAgICkKCgojIFdoeSBhcmUgdGhlc2UgZXh0cmVtZXM/ICBBaXJwb3J0cz8gIEJ1cyBjb2xsZWN0aW9uIHBvaW50cz8KVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgNDk0MDQ0LCA0OTQwNjQpIHwgIyA0OTQwNTQKICAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgNDk0MjczLCA0OTQyOTMpIHwgIyA0OTQyODMKICAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgNDk0NjI2LCA0OTQ2NDYpIHwgIyA0OTQ2MzYKICAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgMTYxMDE1NiwgMTYxMDE3NikgfCAjIDE2MTAxNjYKICAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgMjA3MzA3NCwgMjA3MzA5NCkgIyAyMDczMDg0CiAgICAgICAgICAgKQogICAgKQoKIyBCZWZvcmUgUmVtb3ZpbmcgUnVucwojIFZpZXcoZmlsdGVyKEFsbERheXNfU29ydGVkLAojICAgICAgICAgICAgIGJldHdlZW4oUm93TnVtX09HLCA0OTQwNDQsIDQ5NDA2NCkgfCAjIDQ5NDA1NAojICAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDQ5NDI3MywgNDk0MjkzKSB8ICMgNDk0MjgzCiMgICAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgNDk0NjI2LCA0OTQ2NDYpIHwgIyA0OTQ2MzYKIyAgICAgICAgICAgICAgIGJldHdlZW4oUm93TnVtX09HLCAxNjEwMTU2LCAxNjEwMTc2KSB8ICMgMTYxMDE2NgojICAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDIwNzMwNzQsIDIwNzMwOTQpICMgMjA3MzA4NAojICAgICAgICAgICAgKQojICAgICApCgojIEFmdGVyIFJlbW92aW5nIFJ1bnMKIyBWaWV3KGZpbHRlcihBbGxEYXlzX0ZpcnN0U3RvcElELAojICAgICAgICAgICAgIGJldHdlZW4oUm93TnVtX09HLCA0OTQwNDQsIDQ5NDA2NCkgfCAjIDQ5NDA1NAojICAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDQ5NDI3MywgNDk0MjkzKSB8ICMgNDk0MjgzCiMgICAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgNDk0NjI2LCA0OTQ2NDYpIHwgIyA0OTQ2MzYKIyAgICAgICAgICAgICAgIGJldHdlZW4oUm93TnVtX09HLCAxNjEwMTU2LCAxNjEwMTc2KSB8ICMgMTYxMDE2NgojICAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDIwNzMwNzQsIDIwNzMwOTQpICMgMjA3MzA4NAojICAgICAgICAgICAgKQojICAgICApCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbERpc3RhbmNlX01pLgoKQW55IHJlbGF0aW9uIHdpdGggRGlyQ2hhbmdlMj8gIERvZXNuJ3QgbG9vayBhcyBpZiB0aGlzIGlzIHNvLgpgYGB7cn0KCkV4dHJlbWVUcmF2RGlzdCA8LSBmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICAhaXMubmEoVHJhdmVsRGlzdGFuY2VfTWkpCiAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBtdXRhdGUoVHJhdkRpc3RfRXh0cmVtZSA9IGlmZWxzZShUcmF2ZWxEaXN0YW5jZV9NaSA+IDEuMTU4NzEyMTIxMiwgIyAxLjE1ODcxMjEyMTIgaXMgdGhlIDk5dGggcGVyY2VudGlsZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUcnVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRmFsc2UiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgZ3JvdXBfYnkoRGlyQ2hhbmdlMiwgVHJhdkRpc3RfRXh0cmVtZSkgJT4lIAogIHN1bW1hcmlzZShUcmF2RGlzdE1JX0V4dENudHMgPSBuKCkKICAgICAgICAgICApCgojIEV4dHJlbWVUcmF2RGlzdAoKCkV4dHJlbWVUcmF2RGlzdF9TcHJlYWQgPC0gYXMuZGF0YS5mcmFtZShzcHJlYWQoRXh0cmVtZVRyYXZEaXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZEaXN0X0V4dHJlbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdkRpc3RNSV9FeHRDbnRzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHNlbGVjdChGYWxzZSwKICAgICAgICAgVHJ1ZQogICAgICAgICkKCnJvdy5uYW1lcyhFeHRyZW1lVHJhdkRpc3RfU3ByZWFkKSA8LSBjKCJDaGFuZ2UiLCAiU2FtZSIpCiMgc3RyKEV4dHJlbWVUcmF2RGlzdF9TcHJlYWQpCiMgRXh0cmVtZVRyYXZEaXN0X1NwcmVhZAoKcHJvcC50YWJsZShhcy50YWJsZShhcy5tYXRyaXgoRXh0cmVtZVRyYXZEaXN0X1NwcmVhZCkKICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgMQogICAgICAgICAgKQoKcHJvcC50YWJsZShhcy50YWJsZShhcy5tYXRyaXgoRXh0cmVtZVRyYXZEaXN0X1NwcmVhZCkKICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgMgogICAgICAgICAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaS4KCkxvb2tpbmcgYXQgc3BlY2lmaWMgYnVzZXMgYW5kIFN0YXJ0U3RvcF9JRC4KYGBge3J9CgpybShFeHRyZW1lVHJhdkRpc3QsIEV4dHJlbWVUcmF2RGlzdF9TcHJlYWQpCgpWaWV3KGFycmFuZ2UoZ3JvdXBfYnkoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgICAgICAgICAgIEJ1c19JRAogICAgICAgICAgICAgICAgICAgICApICU+JSAKICAgICAgICAgICAgICAgc3VtbWFyaXNlKERpc3RUcmF2X01lYW4gPSBtZWFuKFRyYXZlbERpc3RhbmNlX01pLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICAgRGlzdFRyYXZfTWVkID0gbWVkaWFuKFRyYXZlbERpc3RhbmNlX01pLCBuYS5ybSA9IFRSVUUpCiAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICBkZXNjKERpc3RUcmF2X01lZCkKICAgICAgICAgICAgKQogICAgKQoKCiMgZXhhbXBsZSBvZiBleHRyZW1lbHkgc21hbGwgVHJhdmVsRGlzdGFuY2VfTWkgdmFsdWVzIChsb29rcyBsaWtlIHRoZSBvZG9tZXRlciB3YXNuJ3QgZnVuY3Rpb25pbmcpClZpZXcoZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgIEJ1c19JRCA9PSA2MTExIHwKICAgICAgICAgICAgICBCdXNfSUQgPT0gNzIwMSB8CiAgICAgICAgICAgICAgQnVzX0lEID09IDgwNTgKICAgICAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoQnVzX0lELCBFdmVudF9UaW1lKQogICAgKQoKClZpZXcoYXJyYW5nZShncm91cF9ieShBbGxEYXlzX05ld09yZGVyLAogICAgICAgICAgICAgICAgICAgICAgU3RhcnRTdG9wX0lECiAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogICAgICAgICAgICAgICBzdW1tYXJpc2UoRGlzdFRyYXZfTWVhbiA9IG1lYW4oVHJhdmVsRGlzdGFuY2VfTWksIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgICAgICAgICAgICBEaXN0VHJhdl9NZWQgPSBtZWRpYW4oVHJhdmVsRGlzdGFuY2VfTWksIG5hLnJtID0gVFJVRSkKICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgIGRlc2MoRGlzdFRyYXZfTWVkKQogICAgICAgICAgICApCiAgICApCgojIGV4YW1wbGUgb2YgZXh0cmVtZWx5IGxhcmdlIFRyYXZlbERpc3RhbmNlX01pIHZhbHVlcy4uLm5vIGlkZWEgd2h5Li4uClZpZXcoZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgIFN0YXJ0U3RvcF9JRCA9PSAiMTAwMzY2NS0tMTIiIHwKICAgICAgICAgICAgICBTdGFydFN0b3BfSUQgPT0gIjEwMDM2NjUtLTUwMDE5MjUiIHwKICAgICAgICAgICAgICBTdGFydFN0b3BfSUQgPT0gIjMwMDEwMzgtLTMwMDI1NjUiCiAgICAgICAgICAgKSAlPiUgCiAgICAgICBhcnJhbmdlKFN0YXJ0U3RvcF9JRCwgRXZlbnRfVGltZSkKICAgICkKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsRGlzdGFuY2VfTWkgJiBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcuCgpJZiBUcmF2ZWxEaXNudGFjZV9NaSBpcyBiZWxvdyB0aGUgNXRoIHBlcmNlbnRpbGUgZm9yIHRoYXQgU3RhcnRTdG9wX0lELCBvciBpZiBUcmF2ZWxEaXNudGFjZV9NaSBpcyBhYm92ZSB0aGUgOTV0aCBwZXJjZW50aWxlIGZvciB0aGF0IFN0YXJ0U3RvcF9JRCwgb3IgaWYgVHJhdmVsRGlzdGFuY2VfTWkgaXMgTkEgKHdoZW4gdGhlIEJ1c0RheV9FdmVudE51bSAhPTEpLCBjb25zaWRlciB0aGlzIGFuIG91dGxpZXIuICBJbiB0aGlzIGNhc2UsIHJlcGxhY2UgdGhlIHZhbHVlIHdpdGggdGhlIG1lYW4gZm9yIHRoYXQgU3RhcnRTdG9wX0lEIGFuZCBIb3VyR3JvdXAgKFREX01pX1NTSEdfTWVhbl9GKSwgb3IgaWYgdGhlcmUgYXJlIG5vdCBlbm91Z2ggdmFsdWVzIGF0IHRoZSBIb3VyR3JvdXAgbGV2ZWwsIHJlcGxhY2UgaXQgd2l0aCB0aGUgbWVhbiBmb3IgdGhhdCBTdGFydFN0b3BfSUQuCmBgYHtyfQoKIyBWaWV3KHRhaWwoQWxsRGF5c19OZXdPcmRlciwgNTAwKSkKCkFsbERheXNfTmV3VHJhdmVsRGlzdCA8LSAKICBtdXRhdGUoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3ID0gaWZlbHNlKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkgJiAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKFRyYXZlbERpc3RhbmNlX01pIDwgVERfTWlfU1NIR19xNSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pID4gVERfTWlfU1NIR19xOTUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTSEdfQ250X0YgPj0gMjAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU0hHX01lYW5fRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkgJiAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKFRyYXZlbERpc3RhbmNlX01pIDwgVERfTWlfU1NIR19xNSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pID4gVERfTWlfU1NIR19xOTUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTSEdfQ250X0YgPCAyMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTX0NudF9GID49IDIwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NfTWVhbl9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoIWlzLm5hKFRyYXZlbERpc3RhbmNlX01pKSAmIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoVHJhdmVsRGlzdGFuY2VfTWkgPCBURF9NaV9TU0hHX3E1IHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWkgPiBURF9NaV9TU0hHX3E5NQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NfQ250X0YgPCAyMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTX0NudCA+PSAyMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTX01lYW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBCdXNEYXlfRXZlbnROdW0gIT0gMSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX0h2cnMgIT0gMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX0h2cnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBCdXNEYXlfRXZlbnROdW0gIT0gMSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX0h2cnMgPT0gMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTX01lYW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApKSkpKSwKICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3X0xhYmVsID0gCiAgICAgICAgICAgZmFjdG9yKGlmZWxzZSghaXMubmEoVHJhdmVsRGlzdGFuY2VfTWkpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgKFRyYXZlbERpc3RhbmNlX01pIDwgVERfTWlfU1NIR19xNSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pID4gVERfTWlfU1NIR19xOTUKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTSEdfQ250X0YgPj0gMjAsCiAgICAgICAgICAgICAgICAgICAgICAgICAiVERfTWlfU1NIR19NZWFuX0YiLAogICAgICAgICAgICAgICAgICBpZmVsc2UoIWlzLm5hKFRyYXZlbERpc3RhbmNlX01pKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIChUcmF2ZWxEaXN0YW5jZV9NaSA8IFREX01pX1NTSEdfcTUgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaSA+IFREX01pX1NTSEdfcTk1CiAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU0hHX0NudF9GIDwgMjAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU19DbnRfRiA+PSAyMCwKICAgICAgICAgICAgICAgICAgICAgICAgICJURF9NaV9TU19NZWFuX0YiLAogICAgICAgICAgICAgICAgICBpZmVsc2UoIWlzLm5hKFRyYXZlbERpc3RhbmNlX01pKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIChUcmF2ZWxEaXN0YW5jZV9NaSA8IFREX01pX1NTSEdfcTUgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaSA+IFREX01pX1NTSEdfcTk1CiAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU19DbnRfRiA8IDIwICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NfQ250ID49IDIwLAogICAgICAgICAgICAgICAgICAgICAgICAgIlREX01pX1NTX01lYW4iLAogICAgICAgICAgICAgICAgICBpZmVsc2UoaXMubmEoVHJhdmVsRGlzdGFuY2VfTWkpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgQnVzRGF5X0V2ZW50TnVtICE9IDEgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzICE9IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAiVHJhdmVsRGlzdGFuY2VfTWlfSHZycyIsCiAgICAgICAgICAgICAgICAgIGlmZWxzZShpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBCdXNEYXlfRXZlbnROdW0gIT0gMSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX0h2cnMgPT0gMCwKICAgICAgICAgICAgICAgICAgICAgICAgICJURF9NaV9TU19NZWFuIiwKICAgICAgICAgICAgICAgICAgICAgICAgICJUcmF2ZWxEaXN0YW5jZV9NaSIKICAgICAgICAgICAgICAgICAgICAgICAgKSkpKSkKICAgICAgICAgICAgICAgICApLAogICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzID0gaWZlbHNlKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzICE9IDAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKFRyYXZlbERpc3RhbmNlX01pX05ldyA8IFREX01pX3EyIHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ldyA+IFREX01pX3E5OAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ldwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZyc19MYWJlbCA9CiAgICAgICAgICAgZmFjdG9yKGlmZWxzZSghaXMubmEoVHJhdmVsRGlzdGFuY2VfTWlfSHZycykgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzICE9IDAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAoVHJhdmVsRGlzdGFuY2VfTWlfTmV3IDwgVERfTWlfcTIgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcgPiBURF9NaV9xOTgKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICJUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihUcmF2ZWxEaXN0YW5jZV9NaV9OZXdfTGFiZWwpCiAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICApLAogICAgICAgICBTcGVlZEF2Z19NcGhfTmV3SHZycyA9IFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMgLyBUcmF2ZWxUaW1lX0hyCiAgICAgICAgKQoKcm0oQWxsRGF5c19OZXdPcmRlcikKc3RyKEFsbERheXNfTmV3VHJhdmVsRGlzdCkKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsRGlzdGFuY2VfTWkgJiBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzICYgVHJhdmVsRGlzdGFuY2VfTWlfTmV3LgoKUXVpY2sgc3VtbWFyeSBhbmQgdGhlbiBjb3JyZWxhdGlvbiBjYWxjdWxhdGlvbi4KYGBge3J9CgojIDM4IHJvd3MgbWVldCB0aGlzIGNyaXRlcmlhIGFueW1vcmUgIC0tICBhcHBlYXJzIHRvIGJlIHRoZSBjYXNlIHdoZW4gYm90aCB0aGUgTGF0IExvbmcgY2FsY3VsYXRpb25zLCBhbmQgdGhlIFRyYXZlbERpc3RhbmNlIGNhbGN1bGF0aW9ucyBkaWQgbm90IGZ1bmN0aW9uIHByb3Blcmx5LgpWaWV3KGZpbHRlcihBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgIGlzLm5hKFRyYXZlbERpc3RhbmNlX01pX05ldykgJgogICAgICAgICAgICAgIEJ1c0RheV9FdmVudE51bSAhPSAxCiAgICAgICAgICAgKQogICAgKQoKVmlldyhBbGxEYXlzX05ld1RyYXZlbERpc3QgJT4lIAogICAgICAgYXJyYW5nZShkZXNjKFRyYXZlbERpc3RhbmNlX01pX05ldykpICU+JSAKICAgICAgIGhlYWQoNTAwKQogICAgKQoKc3VtbWFyeShzZWxlY3QoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaSwKICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfSHZycywKICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3LAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzCiAgICAgICAgICAgICAgKQogICAgICAgKQoKc3VtbWFyeShzZWxlY3QoZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgICAgICAgIEJ1c0RheV9FdmVudE51bSAhPSAxCiAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pLAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzLAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcsCiAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMKICAgICAgICAgICAgICApCiAgICAgICApCgoKY29yKHNlbGVjdChBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWksCiAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfSHZycywKICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcsCiAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycwogICAgICAgICAgKSwKICAgIHVzZSA9ICJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiCiAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzX0xhYmVsICYgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZyc19MYWJlbC4KClNob3cgaG93IHRoZSBsYWJlbHMgY2hhbmdlZC4KYGBge3J9Cgpncm91cF9ieShBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ld19MYWJlbCwKICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZyc19MYWJlbAogICAgICAgICkgJT4lIAogIHN1bW1hcmlzZShDbnROdW0gPSBuKCksCiAgICAgICAgICAgIENudFBjdCA9IGZvcm1hdChDbnROdW0gLyBucm93KEFsbERheXNfTmV3VHJhdmVsRGlzdCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY2llbnRpZmljID0gOTk5OQogICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgKSAlPiUgCiAgYXJyYW5nZShkZXNjKENudFBjdCkKICAgICAgICAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaSAmIFRyYXZlbERpc3RhbmNlX01pX0h2cnMgJiBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcuCgpHcmFwaGluZyB0aGUgdHdvIG1ldGhvZHMgb2YgY2FsY3VsYXRpbmcgVHJhdmVsRGlzdGFuY2VfTWkuCgpGaXJzdCwgbGV0J3MgZ2V0IGNyZWF0ZSBhIGZ1bmN0aW9uIHRvIHBsb3QgdGhlIGxpbmVyIG1vZGVsIGVxdWF0aW9uLgpgYGB7cn0KCmxtX2VxbiA8LSBmdW5jdGlvbihkZiwgeSwgeCl7CiAgbSA8LSBsbSh5IH4geCwgZGYpCiAgCiAgbCA8LSBsaXN0KGEgPSBmb3JtYXQoY29lZihtKVsxXSwgZGlnaXRzID0gMiksCiAgICAgICAgICAgIGIgPSBmb3JtYXQoYWJzKGNvZWYobSlbMl0pLCBkaWdpdHMgPSAyKSwKICAgICAgICAgICAgczEgPSBpZmVsc2UodGVzdCA9IGNvZWYobSlbMl0gPiAwLAogICAgICAgICAgICAgICAgICAgICAgICB5ZXMgPSAiKyIsCiAgICAgICAgICAgICAgICAgICAgICAgIG5vID0gIi0iCiAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgcjIgPSBmb3JtYXQoc3VtbWFyeShtKSRyLnNxdWFyZWQsCiAgICAgICAgICAgICAgICAgICAgICAgIGRpZ2l0cyA9IDMKICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgKQogIAogIGVxIDwtIHN1YnN0aXR1dGUoaXRhbGljKHkpID09IGF+fnMxfn5iICUuJSBpdGFsaWMoeCkqIiwifn5pdGFsaWMocileMn4iPSJ+cjIsCiAgICAgICAgICAgICAgICAgICBsCiAgICAgICAgICAgICAgICAgICkKICAKICBhcy5jaGFyYWN0ZXIoYXMuZXhwcmVzc2lvbihlcSkKICAgICAgICAgICAgICApICAgICAgICAgICAgIAp9CgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbERpc3RhbmNlX01pICYgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycy4KClNjYXR0ZXIgcGxvdCAodXNpbmcgYSAxMCUgc2FtcGxlIHRvIG1ha2luZyBwbG90dGluZyB0aW1lIGZhc3RlciBhbmQgdG8gcmVkdWNlIHVuLW5lZWRlZCBkYXRhIGluIHRoZSAic2FtZSIgc3Bsb3QpLgpgYGB7cn0KCnNldC5zZWVkKDEyMzQ1Njc4OSkKQWxsRGF5c19OZXdUcmF2ZWxEaXN0XzEwUGN0IDwtIGZpbHRlcihBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIWlzLm5hKFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHJlbmFtZShEaXN0TWV0aG9kID0gVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZyc19MYWJlbCkgJT4lIAogIHNhbXBsZV9mcmFjKDAuMSkKCgpUcmF2RGlzdF9NaVZzQ2FsYyA8LSBnZ3Bsb3Qoc2VsZWN0KEFsbERheXNfTmV3VHJhdmVsRGlzdF8xMFBjdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERpc3RNZXRob2QKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IFRyYXZlbERpc3RhbmNlX01pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IERpc3RNZXRob2QKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKCJyZWQiLCJibHVlIiwgImdyZWVuIiwgIm9yYW5nZSIsICJibGFjayIpCiAgICAgICAgICAgICAgICAgICAgICkgKwogIGdlb21fcG9pbnQoc2hhcGUgPSAxLCBhbHBoYSA9IDAuNSkgKwogIHNjYWxlX3NoYXBlKHNvbGlkID0gRkFMU0UpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBjb2xvdXIgPSAiYmx1ZSIpICsKICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAwLCBzbG9wZSA9IDEsIGNvbG91ciA9ICJyZWQiKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDAsIDEuNSksIHlsaW0gPSBjKDAsIDEuNSkKICAgICAgICAgICAgICAgICApICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEuNSwgMC4yNSkKICAgICAgICAgICAgICAgICAgICApICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEuNSwgMC4yNSkKICAgICAgICAgICAgICAgICAgICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgI2MoMC44NSwgMC40MCksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpCiAgICAgICApICsKICBhbm5vdGF0ZShsYWJlbCA9IGxtX2VxbihkZiA9IEFsbERheXNfTmV3VHJhdmVsRGlzdF8xMFBjdCwKICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gQWxsRGF5c19OZXdUcmF2ZWxEaXN0XzEwUGN0JFRyYXZlbERpc3RhbmNlX01pLAogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBBbGxEYXlzX05ld1RyYXZlbERpc3RfMTBQY3QkVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycwogICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAjIHggPSA2MiwKICAgICAgICAgICAjIHkgPSAyMCwKICAgICAgICAgICB4ID0gMC43MCwKICAgICAgICAgICB5ID0gMC4wMCwKICAgICAgICAgICBnZW9tID0gInRleHQiLAogICAgICAgICAgIHNpemUgPSAzLAogICAgICAgICAgIGNvbG91ciA9ICJibHVlIiwKICAgICAgICAgICBwYXJzZSA9IFRSVUUKICAgICAgICAgICkgKwogIGFubm90YXRlKGxhYmVsID0gIlJlZmVyZW5jZSBMaW5lIChzbG9wZSA9IDEpIiwKICAgICAgICAgICAjIHggPSAxNiwKICAgICAgICAgICAjIHkgPSAzMCwKICAgICAgICAgICB4ID0gMC44MCwKICAgICAgICAgICB5ID0gMS4wNSwKICAgICAgICAgICBnZW9tID0gInRleHQiLAogICAgICAgICAgIHNpemUgPSAzLAogICAgICAgICAgIGNvbG91ciA9ICJyZWQiCiAgICAgICAgICApICsKICBsYWJzKHRpdGxlID0gIlRyYXZlbERpc3RhbmNlX01pIHZzLiBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzIiwKICAgICAgIHggPSAiVHJhdmVsRGlzdGFuY2VfTWkiLAogICAgICAgeSA9ICJUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzIgogICAgICApCiMgKwojICAgZ2VvbV9qaXR0ZXIoKQoKVHJhdkRpc3RfTWlWc0NhbGMKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsRGlzdGFuY2VfTWkgJiBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzICYgVHJhdmVsRGlzdGFuY2VfTWlfTmV3LgoKR3JhcGhpbmcgdGVzdCB3aXRoIHJib2tlaC4KYGBge3J9CgpUcmF2RGlzdF9NaVZzQ2FsY19Cb2tlaCA8LSBmaWd1cmUoZGF0YSA9IHNlbGVjdChBbGxEYXlzX05ld1RyYXZlbERpc3RfMTBQY3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEaXN0TWV0aG9kCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhsaW0gPSBjKDAsIDEuNSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5bGltID0gYygwLCAxLjUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kX2xvY2F0aW9uID0gImJvdHRvbV9yaWdodCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbHlfcG9pbnRzKHggPSBUcmF2ZWxEaXN0YW5jZV9NaSwKICAgICAgICAgICAgeSA9IFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMsCiAgICAgICAgICAgIGNvbG9yID0gRGlzdE1ldGhvZCwKICAgICAgICAgICAgaG92ZXIgPSBjKFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMsIFRyYXZlbERpc3RhbmNlX01pLCBEaXN0TWV0aG9kKQogICAgICAgICAgICkgJT4lIAogIGx5X2FibGluZShhID0gMCwgYiA9IDEsIGNvbG9yID0gInJlZCIpCgpUcmF2RGlzdF9NaVZzQ2FsY19Cb2tlaAoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcuCgpDYWxjdWxhdGluZyB0aGUgbWluaW11bSBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcgdmFsdWUgYXQgZWFjaCBwZXJjZW50aWxlLgpgYGB7cn0KCnJtKFRyYXZEaXN0X01pVnNDYWxjX0Jva2VoKQpybShBbGxEYXlzX05ld1RyYXZlbERpc3RfMTBQY3QpCgoKc3VtbWFyeShzZWxlY3QoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaSwKICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfSHZycywKICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3LAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzCiAgICAgICAgICAgICAgKQogICAgICAgKQoKc3VtbWFyeShzZWxlY3QoZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgICAgICAgIEJ1c0RheV9FdmVudE51bSAhPSAxCiAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pLAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzLAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcsCiAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMKICAgICAgICAgICAgICApCiAgICAgICApCgoKVHJhdkRpc3RNaU5fTnRpbGUgPC0gYXMuZGF0YS5mcmFtZShzZWxlY3QoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGFydFN0b3BfSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ld19MYWJlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzX0xhYmVsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFBjdFJfTiA9IHBlcmNlbnRfcmFuayhBbGxEYXlzX05ld1RyYXZlbERpc3QkVHJhdmVsRGlzdGFuY2VfTWlfTmV3KSwKICAgICAgICAgIyBQY3RSX0ggPSBwZXJjZW50X3JhbmsoQWxsRGF5c19OZXdUcmF2ZWxEaXN0JFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMpLAogICAgICAgICBQY3RSX1JvdW5kX04gPSByb3VuZChQY3RSX04sIDIpCiAgICAgICAgICMgUGN0Ul9Sb3VuZF9IID0gcm91bmQoUGN0Ul9ILCAyKQogICAgICAgICkgCgojIHN0cihUcmF2RGlzdE1pTl9OdGlsZSkKIyBWaWV3KGhlYWQoVHJhdkRpc3RNaU5fTnRpbGUsIDUwMCkpCgpUcmF2RGlzdE1pTl9OdGlsZV9Sb3dzIDwtIG5yb3coVHJhdkRpc3RNaU5fTnRpbGUpCgojIFZpZXcodGFpbChUcmF2RGlzdE1pTl9OdGlsZSwgNTAwKSkKCgpUcmF2RGlzdE1pTl9QY3RpbGVzIDwtIGdyb3VwX2J5KFRyYXZEaXN0TWlOX050aWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBjdFJfUm91bmRfTgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKAogICAgTWluVERNaUF0UGN0aWxlX04gPSBtaW4oVHJhdmVsRGlzdGFuY2VfTWlfTmV3KSwKICAgICMgTWluVERNaUF0UGN0aWxlX0ggPSBtaW4oVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycyksCiAgICBDbnRzQXRQY3RpbGVfTiA9IHN1bSghaXMubmEoVHJhdmVsRGlzdGFuY2VfTWlfTmV3KSksCiAgICAjIENudHNBdFBjdGlsZV9IID0gc3VtKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzKSksCiAgICBQY3RzQXRQY3RpbGVfTiA9IENudHNBdFBjdGlsZV9OIC8gVHJhdkRpc3RNaU5fTnRpbGVfUm93cwogICAgIyBQY3RzQXRQY3RpbGVfSCA9IENudHNBdFBjdGlsZV9IIC8gVHJhdkRpc3RNaU5fTnRpbGVfUm93cwogICkgJT4lIAogIG11dGF0ZShDdW1TdW1QQXRQX04gPSBjdW1zdW0oUGN0c0F0UGN0aWxlX04pCiAgICAgICAgICMgQ3VtU3VtUEF0UF9IID0gY3Vtc3VtKFBjdHNBdFBjdGlsZV9IKQogICAgICAgICkKCiMgVmlldyhUcmF2RGlzdE1pTl9QY3RpbGVzKQpUcmF2RGlzdE1pTl9QY3RpbGVzCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMKCkNhbGN1bGF0aW5nIHRoZSBtaW5pbXVtIFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMgdmFsdWUgYXQgZWFjaCBwZXJjZW50aWxlLgpgYGB7cn0KClRyYXZEaXN0TWlIX050aWxlIDwtIGFzLmRhdGEuZnJhbWUoc2VsZWN0KEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RhcnRTdG9wX0lELAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFRyYXZlbERpc3RhbmNlX01pX05ld19MYWJlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZyc19MYWJlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBtdXRhdGUoIyBQY3RSX04gPSBwZXJjZW50X3JhbmsoQWxsRGF5c19OZXdUcmF2ZWxEaXN0JFRyYXZlbERpc3RhbmNlX01pX05ldyksCiAgICAgICAgIFBjdFJfSCA9IHBlcmNlbnRfcmFuayhBbGxEYXlzX05ld1RyYXZlbERpc3QkVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycyksCiAgICAgICAgICMgUGN0Ul9Sb3VuZF9OID0gcm91bmQoUGN0Ul9OLCAyKSwKICAgICAgICAgUGN0Ul9Sb3VuZF9IID0gcm91bmQoUGN0Ul9ILCAyKQogICAgICAgICkgCgojIHN0cihUcmF2RGlzdE1pSF9OdGlsZSkKIyBWaWV3KGhlYWQoVHJhdkRpc3RNaUhfTnRpbGUsIDUwMCkpCgpUcmF2RGlzdE1pSF9OdGlsZV9Sb3dzIDwtIG5yb3coVHJhdkRpc3RNaUhfTnRpbGUpCgojIFZpZXcodGFpbChUcmF2RGlzdE1pSF9OdGlsZSwgNTAwKSkKCgpUcmF2RGlzdE1pSF9QY3RpbGVzIDwtIGdyb3VwX2J5KFRyYXZEaXN0TWlIX050aWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBjdFJfUm91bmRfSAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKAogICAgIyBNaW5URE1pQXRQY3RpbGVfTiA9IG1pbihUcmF2ZWxEaXN0YW5jZV9NaV9OZXcpLAogICAgTWluVERNaUF0UGN0aWxlX0ggPSBtaW4oVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycyksCiAgICAjIENudHNBdFBjdGlsZV9OID0gc3VtKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaV9OZXcpKSwKICAgIENudHNBdFBjdGlsZV9IID0gc3VtKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzKSksCiAgICAjIFBjdHNBdFBjdGlsZV9OID0gQ250c0F0UGN0aWxlX04gLyBUcmF2RGlzdE1pSF9OdGlsZV9Sb3dzLAogICAgUGN0c0F0UGN0aWxlX0ggPSBDbnRzQXRQY3RpbGVfSCAvIFRyYXZEaXN0TWlIX050aWxlX1Jvd3MKICApICU+JSAKICBtdXRhdGUoIyBDdW1TdW1QQXRQX04gPSBjdW1zdW0oUGN0c0F0UGN0aWxlX04pLAogICAgICAgICBDdW1TdW1QQXRQX0ggPSBjdW1zdW0oUGN0c0F0UGN0aWxlX0gpCiAgICAgICAgKQoKIyBWaWV3KFRyYXZEaXN0TWlIX1BjdGlsZXMpClRyYXZEaXN0TWlIX1BjdGlsZXMKCmBgYAoKCkpvaW4gVHJhdkRpc3RNaUhfUGN0aWxlcywgVHJhdkRpc3RNaU5fUGN0aWxlcywgYW5kIFRyYXZEaXN0TWlfUGN0aWxlcy4KCn4xMSUgb2YgcmlkZXMgYXJlIHN0aWxsIHNob3dpbmcgYXMgbGVzcyB0aGFuIDAuMSBtaWxlcyBvZiBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzLgpgYGB7cn0KCnJtKFRyYXZEaXN0TWlOX050aWxlX1Jvd3MsIFRyYXZEaXN0TWlIX050aWxlX1Jvd3MsIFRyYXZEaXN0TWlOX050aWxlLCBUcmF2RGlzdE1pSF9OdGlsZSkKCgojIFZpZXcoVHJhdkRpc3RNaV9QY3RpbGVzKQojIFZpZXcoVHJhdkRpc3RNaU5fUGN0aWxlcykKIyBWaWV3KFRyYXZEaXN0TWlIX1BjdGlsZXMpCgpUcmF2RGlzdE1pX1BjdGlsZXNfQWxsIDwtIGlubmVyX2pvaW4oeCA9IFRyYXZEaXN0TWlfUGN0aWxlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBUcmF2RGlzdE1pTl9QY3RpbGVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCJQY3RSX1JvdW5kIiA9ICJQY3RSX1JvdW5kX04iKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBpbm5lcl9qb2luKHkgPSBUcmF2RGlzdE1pSF9QY3RpbGVzLAogICAgICAgICAgICAgYnkgPSBjKCJQY3RSX1JvdW5kIiA9ICJQY3RSX1JvdW5kX0giKQogICAgICAgICAgICApICU+JSAKICBzZWxlY3QoUGN0Ul9Sb3VuZCwKICAgICAgICAgTWluVHJhdkRpc3RNaUF0UGN0aWxlLAogICAgICAgICBNaW5URE1pQXRQY3RpbGVfTiwKICAgICAgICAgTWluVERNaUF0UGN0aWxlX0gsCiAgICAgICAgIENudHNBdFBjdGlsZSwKICAgICAgICAgQ250c0F0UGN0aWxlX04sCiAgICAgICAgIENudHNBdFBjdGlsZV9ILAogICAgICAgICBQY3RzQXRQY3RpbGUsCiAgICAgICAgIFBjdHNBdFBjdGlsZV9OLAogICAgICAgICBQY3RzQXRQY3RpbGVfSCwKICAgICAgICAgQ3VtU3VtUEF0UCwKICAgICAgICAgQ3VtU3VtUEF0UF9OLAogICAgICAgICBDdW1TdW1QQXRQX0gKICAgICAgICAgKQoKIyBzdHIoVHJhdkRpc3RNaV9QY3RpbGVzX0FsbCkKCnJtKFRyYXZEaXN0TWlfUGN0aWxlcywgVHJhdkRpc3RNaU5fUGN0aWxlcyxUcmF2RGlzdE1pSF9QY3RpbGVzKQoKClZpZXcoVHJhdkRpc3RNaV9QY3RpbGVzX0FsbCkKVHJhdkRpc3RNaV9QY3RpbGVzX0FsbAoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcuCgpXaHkgYXJlIHRoZXJlIHN0aWxsIHNvbWUgc21hbGwgb3IgbGFyZ2UgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycyB2YWx1ZXMuCmBgYHtyfQoKIyBWaWV3KGZpbHRlcihBbGxEYXlzX05ld1RyYXZlbERpc3QsCiMgICAgICAgICAgICAgIWlzLm5hKFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMpCiMgICAgICAgICAgICApICU+JSAKIyAgICAgICAgc2VsZWN0KC1URF9NaV9xMiwKIyAgICAgICAgICAgICAgIC1URF9NaV9xOTgsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NfcTUsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NfcTk1LAojICAgICAgICAgICAgICAgLVREX01pX1NTSEdfcTUsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NIR19xOTUsCiMgICAgICAgICAgICAgICAtVERfTWlfTWVhbiwKIyAgICAgICAgICAgICAgIC1URF9NaV9NZWFuX0YsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NfTWVhbiwKIyAgICAgICAgICAgICAgIC1URF9NaV9TU19NZWFuX0YsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NIR19NZWFuLAojICAgICAgICAgICAgICAgLVREX01pX1NTSEdfTWVhbl9GLAojICAgICAgICAgICAgICAgLVREX01pX01lZCwKIyAgICAgICAgICAgICAgIC1URF9NaV9NZWRfRiwKIyAgICAgICAgICAgICAgIC1URF9NaV9TU19NZWQsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NfTWVkX0YsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NIR19NZWQsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NIR19NZWRfRiwKIyAgICAgICAgICAgICAgIC1URF9NaV9DbnQsCiMgICAgICAgICAgICAgICAtVERfTWlfQ250X0YsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NfQ250LAojICAgICAgICAgICAgICAgLVREX01pX1NTX0NudF9GLAojICAgICAgICAgICAgICAgLVREX01pX1NTSEdfQ250LAojICAgICAgICAgICAgICAgLVREX01pX1NTSEdfQ250X0YsCiMgICAgICAgICAgICAgICAtVFRfU2VjX3EyLAojICAgICAgICAgICAgICAgLVRUX1NlY19xOTgsCiMgICAgICAgICAgICAgICAtVFRfU2VjX1NTX3E1LAojICAgICAgICAgICAgICAgLVRUX1NlY19TU19xOTUsCiMgICAgICAgICAgICAgICAtVFRfU2VjX1NTSEdfcTUsCiMgICAgICAgICAgICAgICAtVFRfU2VjX1NTSEdfcTk1LAojICAgICAgICAgICAgICAgLVRUX1NlY19NZWFuLAojICAgICAgICAgICAgICAgLVRUX1NlY19NZWFuX0YsCiMgICAgICAgICAgICAgICAtVFRfU2VjX1NTX01lYW4sCiMgICAgICAgICAgICAgICAtVFRfU2VjX1NTX01lYW5fRiwKIyAgICAgICAgICAgICAgIC1UVF9TZWNfU1NIR19NZWFuLAojICAgICAgICAgICAgICAgLVRUX1NlY19TU0hHX01lYW5fRiwKIyAgICAgICAgICAgICAgIC1UVF9TZWNfTWVkLAojICAgICAgICAgICAgICAgLVRUX1NlY19NZWRfRiwKIyAgICAgICAgICAgICAgIC1UVF9TZWNfU1NfTWVkLAojICAgICAgICAgICAgICAgLVRUX1NlY19TU19NZWRfRiwKIyAgICAgICAgICAgICAgIC1UVF9TZWNfU1NIR19NZWQsCiMgICAgICAgICAgICAgICAtVFRfU2VjX1NTSEdfTWVkX0YsCiMgICAgICAgICAgICAgICAtVFRfU2VjX0NudCwKIyAgICAgICAgICAgICAgIC1UVF9TZWNfQ250X0YsCiMgICAgICAgICAgICAgICAtVFRfU2VjX1NTX0NudCwKIyAgICAgICAgICAgICAgIC1UVF9TZWNfU1NfQ250X0YsCiMgICAgICAgICAgICAgICAtVFRfU2VjX1NTSEdfQ250LAojICAgICAgICAgICAgICAgLVRUX1NlY19TU0hHX0NudF9GLAojICAgICAgICAgICAgICAgLVRUX0hyX3EyLAojICAgICAgICAgICAgICAgLVRUX0hyX3E5OCwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU19xNSwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU19xOTUsCiMgICAgICAgICAgICAgICAtVFRfSHJfU1NIR19xNSwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU0hHX3E5NSwKIyAgICAgICAgICAgICAgIC1UVF9Icl9NZWFuLAojICAgICAgICAgICAgICAgLVRUX0hyX01lYW5fRiwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU19NZWFuLAojICAgICAgICAgICAgICAgLVRUX0hyX1NTX01lYW5fRiwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU0hHX01lYW4sCiMgICAgICAgICAgICAgICAtVFRfSHJfU1NIR19NZWFuX0YsCiMgICAgICAgICAgICAgICAtVFRfSHJfTWVkLAojICAgICAgICAgICAgICAgLVRUX0hyX01lZF9GLAojICAgICAgICAgICAgICAgLVRUX0hyX1NTX01lZCwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU19NZWRfRiwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU0hHX01lZCwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU0hHX01lZF9GLAojICAgICAgICAgICAgICAgLVRUX0hyX0NudCwKIyAgICAgICAgICAgICAgIC1UVF9Icl9DbnRfRiwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU19DbnQsCiMgICAgICAgICAgICAgICAtVFRfSHJfU1NfQ250X0YsCiMgICAgICAgICAgICAgICAtVFRfSHJfU1NIR19DbnQsCiMgICAgICAgICAgICAgICAtVFRfSHJfU1NIR19DbnRfRgojICAgICAgICAgICAgICApICU+JSAKIyAgICAgICAgYXJyYW5nZShUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzKSAlPiUgCiMgICAgICAgIGhlYWQoNTAwKQojICAgICApCgpWaWV3KGZpbHRlcihBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgICFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzKQogICAgICAgICAgICkgJT4lIAogICAgICAgc2VsZWN0KC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICAgICkgJT4lIAogICAgICAgYXJyYW5nZShUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzKSAlPiUKICAgICAgIGhlYWQoNTAwKQogICAgKQoKIyBleGFtcGxlcyBvZiB0aGUgc21hbGxlc3QgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycyB2YWx1ZXMuClZpZXcoZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgKFJvd051bV9PRyA+PSAxNDI0NDQwICYgUm93TnVtX09HIDw9IDE0MjQ0NjApIHwgIyAxNDI0NDUwICAtLSAgZGlyZWN0aW9uIGNoYW5nZQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSA3NjMyOTIgJiBSb3dOdW1fT0cgPD0gNzYzMzEyKSB8ICMgNzYzMzAyICAtLSAgZGlyZWN0aW9uIGNoYW5nZQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAxNjc5MDkzICYgUm93TnVtX09HIDw9IDE2NzkxMTMpIHwgIyAxNjc5MTAzICAtLSAgZGlyZWN0aW9uIGNoYW5nZQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAyODYwOTE4ICYgUm93TnVtX09HIDw9IDI4NjA5MzgpICMgMjg2MDkyOCAgLS0gIGxvb2tzIGNvcnJlY3QKICAgICAgICAgICApICU+JSAKICAgICAgIHNlbGVjdCgtbWF0Y2hlcygiKHEoMnw1fCg5NSl8KDk4KSkpfE1lYW58TWVkfENudCIpCiAgICAgICAgICAgICApCiAgICApCgoKVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAhaXMubmEoVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycykKICAgICAgICAgICApICU+JSAKICAgICAgIHNlbGVjdCgtbWF0Y2hlcygiKHEoMnw1fCg5NSl8KDk4KSkpfE1lYW58TWVkfENudCIpCiAgICAgICAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoZGVzYyhUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzKQogICAgICAgICAgICAgICkgJT4lCiAgICAgICBoZWFkKDUwMCkKICAgICkKCiMgZXhhbXBsZXMgb2YgdGhlIGxhcmdlc3QgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycyB2YWx1ZXMuClZpZXcoZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgKFJvd051bV9PRyA+PSAxMDkyMDAwICYgUm93TnVtX09HIDw9IDEwOTIwNTApIHwgIyAxMDkyMDMwICAtLSAgZGlyZWN0aW9uIGNoYW5nZQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAxNjA5NDYwICYgUm93TnVtX09HIDw9IDE2MDk0ODApIHwgIyAxNjA5NDcwICAtLSBkaXJlY3Rpb24gY2hhbmdlIAogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSA1MDg5MDQgJiBSb3dOdW1fT0cgPD0gNTA4OTI0KSB8ICMgNTA4OTE0ICAtLSAgZGlyZWN0aW9uIGNoYW5nZSAmIG9yaWdpbmFsIFN0b3BJRCB3YXMgYmFkCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDI0NzYzNDUgJiBSb3dOdW1fT0cgPD0gMjQ3NjM2NSkgIyAyNDc2MzU1ICAtLSAgZGlyZWN0aW9uIGNoYW5nZQogICAgICAgICAgICkgJT4lIAogICAgICAgc2VsZWN0KC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICAgICkKICAgICkKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsVGltZV9Ici4KClZpZXcoVHJhdkRpc3RNaV9QY3RpbGVzKTogOTglIG9mIFRyYXZlbFRpbWVfSHIgYXJlIGJldHdlZW4gNyBzZWNvbmRzIGFuZCA0NjQgc2Vjb25kcyAofjggbWludXRlcykuCmBgYHtyfQoKVHJhdlRpbWVIcl9OdGlsZSA8LSBzZWxlY3QoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxUaW1lX0hyCiAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKCMgUGN0aWxlID0gbnRpbGUoQWxsRGF5c19OZXdUcmF2ZWxEaXN0JFRyYXZlbFRpbWVfSHIsIDEwMCksCiAgICAgICAgICMgTWluUiA9IG1pbl9yYW5rKEFsbERheXNfTmV3VHJhdmVsRGlzdCRUcmF2ZWxUaW1lX0hyKSwKICAgICAgICAgUGN0UiA9IHBlcmNlbnRfcmFuayhBbGxEYXlzX05ld1RyYXZlbERpc3QkVHJhdmVsVGltZV9IciksCiAgICAgICAgIFBjdFJfUm91bmQgPSByb3VuZChQY3RSLCAyKQogICAgICAgICkgCgojIHN0cihUcmF2VGltZUhyX050aWxlKQoKVHJhdlRpbWVIcl9OdGlsZV9Sb3dzIDwtIG5yb3coVHJhdlRpbWVIcl9OdGlsZSkKCiMgVmlldyh0YWlsKFRyYXZUaW1lSHJfTnRpbGUsIDUwMCkpCgoKVHJhdlRpbWVIcl9QY3RpbGVzIDwtIGdyb3VwX2J5KFRyYXZUaW1lSHJfTnRpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQY3RSX1JvdW5kCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHN1bW1hcmlzZSgKICAgIE1pblRyYXZUaW1lSHJBdFBjdGlsZSA9IG1pbihUcmF2ZWxUaW1lX0hyKSwKICAgIENudHNBdFBjdGlsZSA9IG4oKSwKICAgIFBjdHNBdFBjdGlsZSA9IENudHNBdFBjdGlsZSAvIFRyYXZUaW1lSHJfTnRpbGVfUm93cwogICkgJT4lIAogIG11dGF0ZShDdW1TdW1QQXRQID0gY3Vtc3VtKFBjdHNBdFBjdGlsZSksCiAgICAgICAgIE1pblRyYXZUaW1lU2VjQXRQY3RpbGUgPSBNaW5UcmF2VGltZUhyQXRQY3RpbGUgKiAzNjAwCiAgICAgICAgKQoKcm0oVHJhdlRpbWVIcl9OdGlsZV9Sb3dzKQpybShUcmF2VGltZUhyX050aWxlKQpWaWV3KFRyYXZUaW1lSHJfUGN0aWxlcykKVHJhdlRpbWVIcl9QY3RpbGVzCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbFRpbWVfSHIuCgpIaXN0b2dyYW0gb2YgVHJhdmVsVGltZV9TZWMuCmBgYHtyfQoKVHJhdlRpbWVfU2VjX0hpc3REZW4gPC0gZ2dwbG90KGZpbHRlcihzZWxlY3QoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIWlzLm5hKFRyYXZlbFRpbWVfU2VjKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gVHJhdmVsVGltZV9TZWMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IC4uZGVuc2l0eS4uCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSA1LCBmaWxsID0gImxpZ2h0Ymx1ZSIsIGNvbG91ciA9ICJncmV5NjAiLCBzaXplID0gMC4yKSArCiAgZ2VvbV9saW5lKHN0YXQgPSAiZGVuc2l0eSIsIGNvbG91ciA9ICJyZWQiKSArCiAgIyBzdGF0X2JpbihiaW53aWR0aCA9IDUsCiAgIyAgICAgICAgICBnZW9tID0gInRleHQiLAogICMgICAgICAgICAgc2l6ZSA9IDIuNSwKICAjICAgICAgICAgIHZqdXN0ID0gMS41LAogICMgICAgICAgICAgYWVzKGxhYmVsID0gZm9ybWF0KC4uY291bnQuLiwgYmlnLm1hcmsgPSAiLCIpCiAgIyAgICAgICAgICAgICApLAogICMgICAgICAgICApICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwgMTgwKSwgeWxpbSA9IGMoMCwgMC4wMikKICAgICAgICAgICAgICAgICApICsKICAjICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArCiAgbGFicyh0aXRsZSA9ICJWYXJpYXRpb24gaW4gVHJhdmVsIFRpbWUiLAogICAgICAgeCA9ICJUcmF2ZWwgVGltZSAoc2VjKSIsCiAgICAgICB5ID0gIkRlbnNpdHkiCiAgICAgICkKClRyYXZUaW1lX1NlY19IaXN0RGVuCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbFRpbWVfU2VjLgoKVHJhdmVsVGltZV9TZWMgdmFsdWVzIGFyZSBOQS4KYGBge3J9CgpzdW1tYXJ5KEFsbERheXNfTmV3VHJhdmVsRGlzdCRUcmF2ZWxUaW1lX1NlYykKCgpWaWV3KHNlbGVjdChBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgIC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICApICU+JSAKICAgICAgIGZpbHRlcihpcy5uYShUcmF2ZWxUaW1lX1NlYykgJgogICAgICAgICAgICAgICAgQnVzRGF5X0V2ZW50TnVtICE9IDEgICMgVHJhdmVsVGltZSBwdXJwb3NlZnVsbHkgbm90IGNhbGN1bGF0ZWQgaGVyZQogICAgICAgICAgICAgKQogICAgKQoKIyBleGFtcGxlcyBvZiBUcmF2ZWxUaW1lX1NlYyB2YWx1ZXMgdGhhdCBhcmUgTkEuIFRoZXNlIGFyZSBOQSBiZWNhdXNlIHRoZSBFdmVudF9UaW1lICYgRGVwYXJ0dXJlX1RpbWUgcmVhZGluZ3MgYXJlIG5vdCBhY2N1cmF0ZSAoaS5lLiwgdGhlIHByZXZpb3VzIERlcGFydHVyZV9UaW1lIGlzIEJFRk9SRSBvciBFUVVBTCBUTyB0aGUgY3VycmVudCBFdmVudF9UaW1lKS4KVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAoUm93TnVtX09HID49IDkwODA5ICYgUm93TnVtX09HIDw9IDkwODI5KSB8ICMgOTA4MTkKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gOTA4ODEgJiBSb3dOdW1fT0cgPD0gOTA5MDEpIHwgIyA5MDg5MQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAyNTk3MDY2ICYgUm93TnVtX09HIDw9IDI1OTcwODYpIHwgIyAyNTk3MDc2CiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDI2MTMzMDUgJiBSb3dOdW1fT0cgPD0gMjYxMzMyNSkgIyAyNjEzMzE1CiAgICAgICAgICAgKSAlPiUgCiAgICAgICBzZWxlY3QoLW1hdGNoZXMoIihxKDJ8NXwoOTUpfCg5OCkpKXxNZWFufE1lZHxDbnQiKSkKICAgICkKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsVGltZV9TZWMuCgpUcmF2ZWxUaW1lX1NlYyB2YWx1ZXMgYXJlIGV4dHJlbWVseSBzbWFsbC4KYGBge3J9CgpWaWV3KHNlbGVjdChBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgIC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICApICU+JSAKICAgICAgIGZpbHRlcighaXMubmEoVHJhdmVsVGltZV9TZWMpCiAgICAgICAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoVHJhdmVsVGltZV9TZWMsCiAgICAgICAgICAgICAgIGRlc2MoU3BlZWRBdmdfTXBoX05ld0h2cnMpCiAgICAgICAgICAgICAgKSAlPiUKICAgICAgIGhlYWQoNTAwKQogICAgKQoKIyBleGFtcGxlcyB3aGVyZSBUcmF2ZWxUaW1lX1NlYyBpcyBzbWFsbCAoMSBzZWMpIGFuZCBTcGVlZEF2Z19NcGhfTmV3SHZycyBpcyBsYXJnZS4KVmlldyhzZWxlY3QoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAtbWF0Y2hlcygiKHEoMnw1fCg5NSl8KDk4KSkpfE1lYW58TWVkfENudCIpCiAgICAgICAgICAgKSAlPiUgCiAgICAgICBmaWx0ZXIoKFJvd051bV9PRyA+PSAyMjE3MzUzICYgUm93TnVtX09HIDw9IDIyMTczNzMpIHwgIyAyMjE3MzYzCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDMwOTAzMjEgJiBSb3dOdW1fT0cgPD0gMzA5MDM0MSkgfCAjIDMwOTAzMzEKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gODA3NjQgJiBSb3dOdW1fT0cgPD0gODA3ODQpIHwgIyA4MDc3NAogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAzMzg0MCAmIFJvd051bV9PRyA8PSAzMzg2MCkgIyAzMzg1MAogICAgICAgICAgICkKICAgICkKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsVGltZV9TZWMuCgpUcmF2ZWxUaW1lX1NlYyB2YWx1ZXMgYXJlIGV4dHJlbWVseSBsYXJnZS4KYGBge3J9CgpWaWV3KHNlbGVjdChBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgIC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICApICU+JSAKICAgICAgIGZpbHRlcighaXMubmEoVHJhdmVsVGltZV9TZWMpCiAgICAgICAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoZGVzYyhUcmF2ZWxUaW1lX1NlYyksCiAgICAgICAgICAgICAgIFNwZWVkQXZnX01waF9OZXdIdnJzCiAgICAgICAgICAgICAgKSAlPiUKICAgICAgIGhlYWQoNTAwKQogICAgKQoKIyBleGFtcGxlcyB3aGVyZSBUcmF2ZWxUaW1lX1NlYyBpcyBsYXJnZSBhbmQgU3BlZWRBdmdfTXBoX05ld0h2cnMgaXMgc21hbGwuClZpZXcoc2VsZWN0KEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgLW1hdGNoZXMoIihxKDJ8NXwoOTUpfCg5OCkpKXxNZWFufE1lZHxDbnQiKQogICAgICAgICAgICkgJT4lIAogICAgICAgZmlsdGVyKChSb3dOdW1fT0cgPj0gMTAwNzcwMyAmIFJvd051bV9PRyA8PSAxMDA3NzIzKSB8ICMgMTAwNzcxMwogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAyMzczNTY0ICYgUm93TnVtX09HIDw9IDIzNzM1ODQpIHwgIyAyMzczNTc0CiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDg2NDM3OSAmIFJvd051bV9PRyA8PSA4NjQzOTkpIHwgIyA4NjQzODkKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMjU3MDA2MCAmIFJvd051bV9PRyA8PSAyNTcwMDgwKSAjIDI1NzAwNzAKICAgICAgICAgICApCiAgICApCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbFRpbWVfU2VjLgoKQXJlIGxhcmdlIFRyYXZlbFRpbWVfU2VjIHZhbHVlcyByZWxhdGVkIHRvIFJvdXRlQ2hhbmdlcz8gTG9va3MgbGlrZWx5LiBXaGVuIHRoZSBCdXMgaW52b2x2ZXMgYSBSb3V0ZSAiY2hhbmdlIiwgdGhlcmUgaXMgYWxtb3N0IHR3aWNlIGFzIGxpa2VseSB0byBiZSBhIGNhc2Ugb2YgYW4gb3V0bGllciBUcmF2ZWxUaW1lX1NlYyB2YWx1ZSAob24gdGhlIGhpZ2ggc2lkZSkuCmBgYHtyfQoKVFRMYXJnZVJ0ZUNobmcgPC0gc2VsZWN0KEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgICAgICAgICAgIC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFRUX091dCA9IGZhY3RvcihpZmVsc2UoVHJhdmVsVGltZV9TZWMgPiA0NjQsICAjIHRoaXMgaXMgdGhlIDk5dGggcGVyY2VudGlsZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJPdXRsaWVyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTm9ybWFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKQoKIyBzdHIoVFRMYXJnZVJ0ZUNobmcpCgoKVFRMYXJnZVJ0ZUNobmdfQ250cyA8LSBncm91cF9ieShUVExhcmdlUnRlQ2huZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSdGVDaGFuZ2UyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX091dAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKENudHMgPSBuKCkKICAgICAgICAgICApCgpUVExhcmdlUnRlQ2huZ19TcHJlYWQgPC0gYXMuZGF0YS5mcmFtZShzcHJlYWQoVFRMYXJnZVJ0ZUNobmdfQ250cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX091dCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENudHMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lCiAgc2VsZWN0KC1SdGVDaGFuZ2UyKQoKcm93Lm5hbWVzKFRUTGFyZ2VSdGVDaG5nX1NwcmVhZCkgPC0gYygiQ2hhbmdlIiwgIlNhbWUiKQojIHN0cihUVExhcmdlUnRlQ2huZ19TcHJlYWQpCgoKIyBXaGVuIHRoZSBCdXMgaW52b2x2ZXMgYSBSb3V0ZSAiY2hhbmdlIiwgdGhlcmUgaXMgYWxtb3N0IHR3aWNlIGFzIGxpa2VseSB0byBiZSBhIGNhc2Ugb2YgYW4gb3V0bGllciBUcmF2ZWxUaW1lX1NlYyB2YWx1ZS4KVFRMYXJnZVJ0ZUNobmdfU3ByZWFkCnByb3AudGFibGUoYXMudGFibGUoYXMubWF0cml4KFRUTGFyZ2VSdGVDaG5nX1NwcmVhZCkKICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgMQogICAgICAgICAgKQoKcHJvcC50YWJsZShhcy50YWJsZShhcy5tYXRyaXgoVFRMYXJnZVJ0ZUNobmdfU3ByZWFkKQogICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAyCiAgICAgICAgICApCgojIHJtKFRUTGFyZ2VSdGVDaG5nLCBUVExhcmdlUnRlQ2huZ19TcHJlYWQpCiAgICAgICAgIApgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbFRpbWVfU2VjLgoKQXJlIGxhcmdlIFRyYXZlbFRpbWVfU2VjIHZhbHVlcyByZWxhdGVkIHRvIFJvdXRlQ2hhbmdlcz8gTG9va3MgbGlrZWx5LgpgYGB7cn0KClZpZXcoZmlsdGVyKFRUTGFyZ2VSdGVDaG5nLAogICAgICAgICAgICAhaXMubmEoVHJhdmVsVGltZV9TZWMpICYKICAgICAgICAgICAgICBSdGVDaGFuZ2UyID09ICJTYW1lIgogICAgICAgICAgICkgJT4lIAogICAgICAgYXJyYW5nZShkZXNjKFRyYXZlbFRpbWVfU2VjKSwKICAgICAgICAgICAgICAgU3BlZWRBdmdfTXBoX05ld0h2cnMKICAgICAgICAgICAgICApICU+JQogICAgICAgaGVhZCg1MDApCiAgICApCgoKIyBleGFtcGxlcyB3aGVyZSBUcmF2ZWxUaW1lX1NlYyBpcyBsYXJnZSBhbmQgU3BlZWRBdmdfTXBoX05ld0h2cnMgaXMgc21hbGwuClZpZXcoZmlsdGVyKFRUTGFyZ2VSdGVDaG5nLAogICAgICAgICAgICAoUm93TnVtX09HID49IDIyNTAyOTAgJiBSb3dOdW1fT0cgPD0gMjI1MDMxMCkgfCAjIDIyNTAzMDAKICAgICAgICAgICAgICAoUm93TnVtX09HID49IDg2NzcxNyAmIFJvd051bV9PRyA8PSA4Njc3MzcpIHwgIyA4Njc3MjcKICAgICAgICAgICAgICAoUm93TnVtX09HID49IDg2NDM3OSAmIFJvd051bV9PRyA8PSA4NjQzOTkpIHwgIyA4NjQzODkKICAgICAgICAgICAgICAoUm93TnVtX09HID49IDgwODM5NSAmIFJvd051bV9PRyA8PSA4MDg0MTUpICMgODA4NDA1CiAgICAgICAgICAgKQogICAgKQoKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsVGltZV9TZWMuCgpJZiBUcmF2ZWxUaW1lX1NlYyBpcyBiZWxvdyB0aGUgNXRoIHBlcmNlbnRpbGUgZm9yIHRoYXQgU3RhcnRTdG9wX0lELCBvciBpZiBUcmF2ZWxUaW1lX1NlYyBpcyBhYm92ZSB0aGUgOTV0aCBwZXJjZW50aWxlIGZvciB0aGF0IFN0YXJ0U3RvcF9JRCwgIGNvbnNpZGVyIHRoaXMgYW4gb3V0bGllci4gIEluIHRoaXMgY2FzZSwgcmVwbGFjZSB0aGUgdmFsdWUgd2l0aCB0aGUgbWVhbiBmb3IgdGhhdCBTdGFydFN0b3BfSUQgYW5kIEhvdXJHcm91cCAoVFRfU2VjX1NTSEdfTWVhbl9GKSwgb3IgaWYgdGhlcmUgYXJlIG5vdCBlbm91Z2ggdmFsdWVzIGF0IHRoZSBIb3VyR3JvdXAgbGV2ZWwsIHJlcGxhY2UgaXQgd2l0aCB0aGUgbWVhbiBmb3IgdGhhdCBTdGFydFN0b3BfSUQuCmBgYHtyfQoKcm0oVFRMYXJnZVJ0ZUNobmcsIFRUTGFyZ2VSdGVDaG5nX0NudHMsIFRUTGFyZ2VSdGVDaG5nX1NwcmVhZCkKCgpOZXdUcmF2VGltZSA8LSBtdXRhdGUoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX05ldyA9IGlmZWxzZSghaXMubmEoVHJhdmVsVGltZV9TZWMpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoVHJhdmVsVGltZV9TZWMgPCBUVF9TZWNfU1NIR19xNSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsVGltZV9TZWMgPiBUVF9TZWNfU1NIR19xOTUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NIR19DbnRfRiA+PSAyMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTSEdfTWVhbl9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZSghaXMubmEoVHJhdmVsVGltZV9TZWMpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoVHJhdmVsVGltZV9TZWMgPCBUVF9TZWNfU1NIR19xNSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsVGltZV9TZWMgPiBUVF9TZWNfU1NIR19xOTUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NIR19DbnRfRiA8IDIwICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NfQ250X0YgPj0gMjAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19NZWFuX0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKCFpcy5uYShUcmF2ZWxUaW1lX1NlYykgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChUcmF2ZWxUaW1lX1NlYyA8IFRUX1NlY19TU0hHX3E1IHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYyA+IFRUX1NlY19TU0hHX3E5NQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19DbnRfRiA8IDIwICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NfQ250ID49IDIwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NfTWVhbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoIWlzLm5hKFRyYXZlbFRpbWVfU2VjKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKFRyYXZlbFRpbWVfU2VjIDwgVFRfU2VjX1NTSEdfcTUgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjID4gVFRfU2VjX1NTSEdfcTk1CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTX0NudF9GIDwgMjAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19DbnQgPCAyMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUnRlQ2hhbmdlMiA9PSAiQ2hhbmdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTkEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSkpKSwKICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX05ld19MYWJlbCA9IAogICAgICAgICAgIGZhY3RvcihpZmVsc2UoIWlzLm5hKFRyYXZlbFRpbWVfU2VjKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIChUcmF2ZWxUaW1lX1NlYyA8IFRUX1NlY19TU0hHX3E1IHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsVGltZV9TZWMgPiBUVF9TZWNfU1NIR19xOTUKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU0hHX0NudF9GID49IDIwLAogICAgICAgICAgICAgICAgICAgICAgICAgIlRUX1NlY19TU0hHX01lYW5fRiIsCiAgICAgICAgICAgICAgICAgIGlmZWxzZSghaXMubmEoVHJhdmVsVGltZV9TZWMpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgKFRyYXZlbFRpbWVfU2VjIDwgVFRfU2VjX1NTSEdfcTUgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYyA+IFRUX1NlY19TU0hHX3E5NQogICAgICAgICAgICAgICAgICAgICAgICAgICApICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTSEdfQ250X0YgPCAyMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19DbnRfRiA+PSAyMCwKICAgICAgICAgICAgICAgICAgICAgICAgICJUVF9TZWNfU1NfTWVhbl9GIiwKICAgICAgICAgICAgICAgICAgaWZlbHNlKCFpcy5uYShUcmF2ZWxUaW1lX1NlYykgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAoVHJhdmVsVGltZV9TZWMgPCBUVF9TZWNfU1NIR19xNSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjID4gVFRfU2VjX1NTSEdfcTk1CiAgICAgICAgICAgICAgICAgICAgICAgICAgICApICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTX0NudF9GIDwgMjAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NfQ250ID49IDIwLAogICAgICAgICAgICAgICAgICAgICAgICAgIlRUX1NlY19TU19NZWFuIiwKICAgICAgICAgICAgICAgICAgaWZlbHNlKCFpcy5uYShUcmF2ZWxUaW1lX1NlYykgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAoVHJhdmVsVGltZV9TZWMgPCBUVF9TZWNfU1NIR19xNSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjID4gVFRfU2VjX1NTSEdfcTk1CiAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NfQ250X0YgPCAyMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19DbnQgPCAyMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFJ0ZUNoYW5nZTIgPT0gIkNoYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBOQSwKICAgICAgICAgICAgICAgICAgICAgICAgICJUcmF2ZWxUaW1lX1NlYyIKICAgICAgICAgICAgICAgICAgICAgICAgKSkpKQogICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICBUVF9Icl9OZXcgPSBUVF9TZWNfTmV3IC8gKDYwICogNjApCiAgICAgICAgICAgKQoKCmRpbShBbGxEYXlzX05ld1RyYXZlbERpc3QpCmRpbShOZXdUcmF2VGltZSkKcm0oQWxsRGF5c19OZXdUcmF2ZWxEaXN0KQoKc3VtbWFyeShzZWxlY3QoTmV3VHJhdlRpbWUsCiAgICAgICAgICAgLW1hdGNoZXMoIihxKDJ8NXwoOTUpfCg5OCkpKXxNZWFufE1lZHxDbnQiKQogICAgICAgICAgKQogICApCgpzdHIoc2VsZWN0KE5ld1RyYXZUaW1lLAogICAgICAgICAgIFRyYXZlbFRpbWVfU2VjLAogICAgICAgICAgIFRUX1NlY19OZXcsCiAgICAgICAgICAgVFRfU2VjX05ld19MYWJlbCwKICAgICAgICAgICBUVF9Icl9OZXcKICAgICAgICAgICkKICAgKQoKCnN1bW1hcnkoc2VsZWN0KE5ld1RyYXZUaW1lLAogICAgICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYywKICAgICAgICAgICAgICAgVFRfU2VjX05ldywKICAgICAgICAgICAgICAgVFRfU2VjX05ld19MYWJlbCwKICAgICAgICAgICAgICAgVFRfSHJfTmV3CiAgICAgICAgICAgICAgKQogICAgICAgKQoKYGBgCgoKVGVzdCBpbnZlc3RpZ2F0aW9uIG9mIGp1c3QgdGhlIFgyIFJvdXRlLiBCb3ggcGxvdHMgZm9yIHRpbWUgYmV0d2VlbiBidXMgYXJyaXZhbHMgKGJ5IEhvdXJHcm91cCkuCmBgYHtyfQoKVmlldyhoZWFkKHNlbGVjdChOZXdUcmF2VGltZSwKICAgICAgICAgICAgICAgICAtbWF0Y2hlcygiKHEoMnw1fCg5NSl8KDk4KSkpfE1lYW58TWVkfENudCIpCiAgICAgICAgICAgICAgICApCiAgICAgICAgICkKICAgICkKClgyIDwtIHNlbGVjdChOZXdUcmF2VGltZSwKICAgICAgICAgICAgIC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICAgKSAlPiUgCiAgZmlsdGVyKFJvdXRlID09ICJYMiIpCgpzdHIoWDIpCgpWaWV3KGhlYWQoYXJyYW5nZShYMiwKICAgICAgICAgICAgICAgICAgQnVzX0lELAogICAgICAgICAgICAgICAgICBFdmVudF9UaW1lCiAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgIDUwMAogICAgICAgICApCiAgICApCgpYMl9CeVN0b3AgPC0gZ3JvdXBfYnkoWDIsCiAgICAgICAgICAgICAgICAgICAgICBTdG9wSURfQ2xlYW4KICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgYXJyYW5nZShTdG9wSURfQ2xlYW4sCiAgICAgICAgICBFdmVudF9UaW1lKSAlPiUgCiAgbXV0YXRlKEV2ZW50X1RpbWVfTDEgPSBsYWcoRXZlbnRfVGltZSksCiAgICAgICAgIFRpbWVUb0V2ZW50X1NlYyA9IGFzLm51bWVyaWMoRXZlbnRfVGltZSAtIEV2ZW50X1RpbWVfTDEpLAogICAgICAgICBUaW1lVG9FdmVudF9NaW4gPSBUaW1lVG9FdmVudF9TZWMgLyA2MAogICAgICAgICkKClZpZXcoaGVhZChYMl9CeVN0b3AsIDUwMCkpCgoKIyBDb3VudF9WYWx1ZXMgaXMgbmVlZGVkIHRvIGRpc3BsYXkgdGhlIG1lZGlhbnMgb24gdGhlIGJveCBwbG90cwpDb3VudF9WYWx1ZXMgPC0gZGRwbHkoYXMuZGF0YS5mcmFtZShYMl9CeVN0b3ApLAogICAgICAgICAgICAgICAgICAgICAgLihFdmVudF9UaW1lX0hyR3JvdXApLAogICAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlLAogICAgICAgICAgICAgICAgICAgICAgVmFsdWVfQ291bnRzID0gbWVkaWFuKFRpbWVUb0V2ZW50X01pbiwgbmEucm0gPSBUUlVFKQogICAgICAgICAgICAgICAgICAgICApCgpUaW1lQnR3RXZlbnRzX1gyX0JveFBsb3QgPC0gZ2dwbG90KHNlbGVjdChhcy5kYXRhLmZyYW1lKFgyX0J5U3RvcCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRpbWVUb0V2ZW50X01pbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9Ickdyb3VwCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoZmFjdG9yKEV2ZW50X1RpbWVfSHJHcm91cCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRpbWVUb0V2ZW50X01pbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArIAogIGdlb21fYm94cGxvdChvdXRsaWVyLmNvbG91cj0icmVkIiwgbm90Y2g9VFJVRSwgbmEucm0gPSBUUlVFKSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBDb3VudF9WYWx1ZXMsCiAgICAgICAgICAgIGFlcyh5ID0gVmFsdWVfQ291bnRzLAogICAgICAgICAgICAgICAgbGFiZWwgPSBmb3JtYXQocm91bmQoVmFsdWVfQ291bnRzLCBkaWdpdHMgPSAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zbWFsbCA9IDEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICApLAogICAgICAgICAgICBzaXplID0gMywKICAgICAgICAgICAgdmp1c3QgPSAtMC41CiAgICAgICAgICAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUpKSArCiAgY29vcmRfY2FydGVzaWFuKCMgeGxpbSA9IGMoMCwgMTgwKSwKICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwgMTIwKQogICAgICAgICAgICAgICAgICkgKwogIGxhYnModGl0bGUgPSAiSG93IE9mdGVuIGFuIFgyIEFycml2ZXMgYXQgYSBHaXZlbiBTdG9wIiwKICAgICAgIHggPSAiSG91ciBHcm91cCIsCiAgICAgICB5ID0gIlRpbWUgQmV0d2VlbiBCdXNzZXMgKG1pbikiCiAgICAgICkKClRpbWVCdHdFdmVudHNfWDJfQm94UGxvdAoKYGBgCgoKVGVzdCBpbnZlc3RpZ2F0aW9uIG9mIGp1c3QgdGhlIFgyIFJvdXRlLiBWaW9saW4gcGxvdHMgZm9yIHRpbWUgYmV0d2VlbiBidXMgYXJyaXZhbHMgKGJ5IEhvdXIgR3JvdXApLgpgYGB7cn0KClRpbWVCdHdFdmVudHNfWDJfVmlvbGluUGxvdCA8LSBnZ3Bsb3Qoc2VsZWN0KGFzLmRhdGEuZnJhbWUoWDJfQnlTdG9wKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGltZVRvRXZlbnRfTWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0hyR3JvdXAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoZmFjdG9yKEV2ZW50X1RpbWVfSHJHcm91cCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRpbWVUb0V2ZW50X01pbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArIAogIGdlb21fdmlvbGluKGRyYXdfcXVhbnRpbGVzID0gYygwLjI1LCAwLjUsIDAuNzUpLAogICAgICAgICAgICAgIHRyaW0gPSBUUlVFLAogICAgICAgICAgICAgIHNjYWxlID0gImNvdW50IiwKICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUsCiAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBOQSwKICAgICAgICAgICAgICBpbmhlcml0LmFlcyA9IFRSVUUKICAgICAgICAgICAgICkgKwogIGdlb21fdGV4dChkYXRhID0gQ291bnRfVmFsdWVzLAogICAgICAgICAgICBhZXMoeSA9IFZhbHVlX0NvdW50cywKICAgICAgICAgICAgICAgIGxhYmVsID0gZm9ybWF0KHJvdW5kKFZhbHVlX0NvdW50cywgZGlnaXRzID0gMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuc21hbGwgPSAxCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgc2l6ZSA9IDIuNSwKICAgICAgICAgICAgdmp1c3QgPSAtMC41CiAgICAgICAgICAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUpKSArCiAgY29vcmRfY2FydGVzaWFuKCMgeGxpbSA9IGMoMCwgMTgwKSwKICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwgODApCiAgICAgICAgICAgICAgICAgKSArCiAgbGFicyh0aXRsZSA9ICJIb3cgT2Z0ZW4gYW4gWDIgQXJyaXZlcyBhdCBhIEdpdmVuIFN0b3AiLAogICAgICAgeCA9ICJIb3VyIEdyb3VwIiwKICAgICAgIHkgPSAiVGltZSBCZXR3ZWVuIEJ1c3NlcyAobWluKSIKICAgICAgKQoKVGltZUJ0d0V2ZW50c19YMl9WaW9saW5QbG90CgpgYGAKCgpUZXN0IGludmVzdGlnYXRpb24gb2YganVzdCB0aGUgWDIgUm91dGUuIEJveCBwbG90cyBmb3IgdGltZSBiZXR3ZWVuIGJ1cyBhcnJpdmFscyAoYnkgWmlwIENvZGUpLgpgYGB7cn0KCiMgQ291bnRfVmFsdWVzIGlzIG5lZWRlZCB0byBkaXNwbGF5IHRoZSBtZWRpYW5zIG9uIHRoZSBib3ggcGxvdHMKQ291bnRfVmFsdWVzX3ogPC0gZGRwbHkoYXMuZGF0YS5mcmFtZShYMl9CeVN0b3ApLAogICAgICAgICAgICAgICAgICAgICAgICAuKFN0b3BfWmlwKSwKICAgICAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlLAogICAgICAgICAgICAgICAgICAgICAgICBWYWx1ZV9Db3VudHMgPSBtZWRpYW4oVGltZVRvRXZlbnRfTWluLCBuYS5ybSA9IFRSVUUpCiAgICAgICAgICAgICAgICAgICAgICAgKQoKVGltZUJ0d0V2ZW50c19YMl9Cb3hQbG90X3ogPC0gZ2dwbG90KHNlbGVjdChhcy5kYXRhLmZyYW1lKFgyX0J5U3RvcCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGltZVRvRXZlbnRfTWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0b3BfWmlwCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGZhY3RvcihTdG9wX1ppcCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGltZVRvRXZlbnRfTWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBmYWN0b3IoU3RvcF9aaXApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgKyAKICBnZW9tX2JveHBsb3Qob3V0bGllci5jb2xvdXI9InJlZCIsIG5vdGNoPVRSVUUsIG5hLnJtID0gVFJVRSkgKwogIGdlb21fdGV4dChkYXRhID0gQ291bnRfVmFsdWVzX3osCiAgICAgICAgICAgIGFlcyh5ID0gVmFsdWVfQ291bnRzLAogICAgICAgICAgICAgICAgbGFiZWwgPSBmb3JtYXQocm91bmQoVmFsdWVfQ291bnRzLCBkaWdpdHMgPSAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zbWFsbCA9IDEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICApLAogICAgICAgICAgICBzaXplID0gMywKICAgICAgICAgICAgdmp1c3QgPSAtMC41CiAgICAgICAgICAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUpKSArCiAgY29vcmRfY2FydGVzaWFuKCMgeGxpbSA9IGMoMCwgMTgwKSwKICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwgMTAwKQogICAgICAgICAgICAgICAgICkgKwogIGxhYnModGl0bGUgPSAiSG93IE9mdGVuIGFuIFgyIEFycml2ZXMgYXQgYSBHaXZlbiBTdG9wIiwKICAgICAgIHggPSAiWmlwIENvZGUgb2YgRGVzdGluYXRpb24iLAogICAgICAgeSA9ICJUaW1lIEJldHdlZW4gQnVzc2VzIChtaW4pIgogICAgICApCgpUaW1lQnR3RXZlbnRzX1gyX0JveFBsb3RfegoKYGBgCgoKVGVzdCBpbnZlc3RpZ2F0aW9uIG9mIGp1c3QgdGhlIFgyIFJvdXRlLiBWaW9saW4gcGxvdHMgZm9yIHRpbWUgYmV0d2VlbiBidXMgYXJyaXZhbHMgKGJ5IFppcCBDb2RlKS4KYGBge3J9CgpUaW1lQnR3RXZlbnRzX1gyX1Zpb2xpblBsb3RfeiA8LSBnZ3Bsb3Qoc2VsZWN0KGFzLmRhdGEuZnJhbWUoWDJfQnlTdG9wKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUaW1lVG9FdmVudF9NaW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RvcF9aaXAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGZhY3RvcihTdG9wX1ppcCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGltZVRvRXZlbnRfTWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBmYWN0b3IoU3RvcF9aaXApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgKyAKICBnZW9tX3Zpb2xpbihkcmF3X3F1YW50aWxlcyA9IGMoMC4yNSwgMC41LCAwLjc1KSwKICAgICAgICAgICAgICB0cmltID0gVFJVRSwKICAgICAgICAgICAgICBzY2FsZSA9ICJjb3VudCIsCiAgICAgICAgICAgICAgbmEucm0gPSBUUlVFLAogICAgICAgICAgICAgIHNob3cubGVnZW5kID0gTkEsCiAgICAgICAgICAgICAgaW5oZXJpdC5hZXMgPSBUUlVFCiAgICAgICAgICAgICApICsKICBnZW9tX3RleHQoZGF0YSA9IENvdW50X1ZhbHVlc196LAogICAgICAgICAgICBhZXMoeSA9IFZhbHVlX0NvdW50cywKICAgICAgICAgICAgICAgIGxhYmVsID0gZm9ybWF0KHJvdW5kKFZhbHVlX0NvdW50cywgZGlnaXRzID0gMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuc21hbGwgPSAxCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgc2l6ZSA9IDIuNSwKICAgICAgICAgICAgdmp1c3QgPSAtMC41CiAgICAgICAgICAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUpKSArCiAgY29vcmRfY2FydGVzaWFuKCMgeGxpbSA9IGMoMCwgMTgwKSwKICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwgNjApCiAgICAgICAgICAgICAgICAgKSArCiAgbGFicyh0aXRsZSA9ICJIb3cgT2Z0ZW4gYW4gWDIgQXJyaXZlcyBhdCBhIEdpdmVuIFN0b3AiLAogICAgICAgeCA9ICJaaXAgQ29kZSBvZiBEZXN0aW5hdGlvbiIsCiAgICAgICB5ID0gIlRpbWUgQmV0d2VlbiBCdXNzZXMgKG1pbikiCiAgICAgICkKClRpbWVCdHdFdmVudHNfWDJfVmlvbGluUGxvdF96CgpgYGAKCgpXYWl0aW5nIHRpbWUgYW5hbHlzZXMuCgpNdW5naW5nIGFuZCBzYW1wbGluZyBkYXRhIHRvIGdvIGZyb20gdGltZSBiZXRlZW4gYnVzZXMgdG8gImF2ZXJhZ2UiIHdhaXRpbmcgdGltZS4KCkZpcnN0LCBnZXQgdGhlIG1heCBhbmQgbWluIHRpbWVzIG9mIGJ1cyBzdG9wcyAoZWFjaCBkYXksIGFuZCBmb3IgZWFjaCByb3V0ZSkuCmBgYHtyfQoKcm0oWDIsIFgyX0J5U3RvcCwgWDJfTG9uZywgWDJfUGN0KQoKClJvdXRlTWluTWF4IDwtIGdyb3VwX2J5KE5ld1RyYXZUaW1lLAogICAgICAgICAgICAgICAgICAgICAgICBSb3V0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9EYXRlCiAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKE1pblRpbWUgPSBtaW4oRXZlbnRfVGltZSksCiAgICAgICAgICAgIE1heFRpbWUgPSBtYXgoRXZlbnRfVGltZSkKICAgICAgICAgICApCgpzdHIoUm91dGVNaW5NYXgpClZpZXcoUm91dGVNaW5NYXgpCgpgYGAKCgpXYWl0aW5nIHRpbWUgYW5hbHlzZXMuCgpNdW5naW5nIGFuZCBzYW1wbGluZyBkYXRhIHRvIGdvIGZyb20gdGltZSBiZXRlZW4gYnVzZXMgdG8gImF2ZXJhZ2UiIHdhaXRpbmcgdGltZS4KCihQdWxscyBoZXJlIGFyZSBkb25lIGJ5IGRheSwgYXMgdGhlIGRhdGEgYXJlIHRvbyBsYXJnZSB0byBkbyBhdCBvbmNlLikKYGBge3J9CgojIFZpZXcoaGVhZChOZXdUcmF2VGltZSwgNTAwKSkKCiMgRm9yIGVhY2ggcmVjb3JkLCBjcmVhdGUgYSByYW5kb20gZGF0ZXRpbWUgYmV0d2VlbiB0aGUgZmlyc3QgYW5kIGxhc3Qgc3RvcCBmb3IgdGhhdCBidXMgcm91dGUgKG9uIHRoYXQgZGF5KS4KZm9yKGkgaW4gMzo3KXsKCnNldC5zZWVkKDEyMzQ1Njc4OSkKU2FtcCA8LSBzZWxlY3QoTmV3VHJhdlRpbWUsCiAgICAgICAgICAgICAgIFJvd051bV9PRywKICAgICAgICAgICAgICAgUm91dGUsCiAgICAgICAgICAgICAgICMgUm91dGVHcm91cCwKICAgICAgICAgICAgICAgRXZlbnRfVGltZV9EYXRlLAogICAgICAgICAgICAgICBTdG9wSURfQ2xlYW4sCiAgICAgICAgICAgICAgIHN0YXJ0c193aXRoKCJFdmVudCIpCiAgICAgICAgICAgICAgKSAlPiUgCiAgZmlsdGVyKEV2ZW50X1RpbWVfRGF0ZSA9PSBpKSAlPiUgICMgbmVlZGVkIHRvIGRvIHRoaXMgZWFjaCBkYXkgKDMtNykgYmVjYXVzZSB0aGUgY29tcGxldGUgZmlsZSB3YXMgdG9vIGxhcmdlIHRvIGRvIGF0IG9uY2UKICBsZWZ0X2pvaW4oUm91dGVNaW5NYXgsCiAgICAgICAgICAgIGJ5ID0gYygiUm91dGUiID0gIlJvdXRlIiwKICAgICAgICAgICAgICAgICAgICJFdmVudF9UaW1lX0RhdGUiID0gIkV2ZW50X1RpbWVfRGF0ZSIKICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICkgJT4lIAogIG11dGF0ZShTYW1wVGltZSA9IGFzX2RhdGV0aW1lKHJ1bmlmKG5yb3coLiksICMyMDAwMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluID0gTWluVGltZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXggPSBNYXhUaW1lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR6ID0gIkFtZXJpY2EvTmV3X1lvcmsiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKSAlPiUgCiAgYXJyYW5nZShSb3V0ZSwKICAgICAgICAgIFN0b3BJRF9DbGVhbiwKICAgICAgICAgIEV2ZW50X1RpbWUKICAgICAgICAgKSAKCiMgc3RyKFNhbXApCiMgVmlldyhoZWFkKFNhbXAsIDUwMCkpCiMgCiMgVmlldygKIyBncm91cF9ieShTYW1wLAojICAgICAgICAgIFJvd051bV9PRwojICAgICAgICAgKSAlPiUKIyAgIHN1bW1hcmlzZShDbnRfTnVtID0gbigpLAojICAgICAgICAgICAgIENudF9QY3QgPSAxMDAgKiBDbnRfTnVtIC8gbnJvdyhTYW1wKQojICAgICAgICAgICAgKSAlPiUKIyAgIGFycmFuZ2UoZGVzYyhDbnRfTnVtKSkKIyApCgoKIyBGb3IgZWFjaCBSb3V0ZSBhbmQgU3RvcElEIGNvbWJpbmF0aW9uLCBnZXQgYWxsIHRoZSBFdmVudF9UaW1lIHZhbHVlcyB0aGF0IGFyZSBhZnRlciB0aGUgU2FtcFRpbWUgdmFsdWUuCiMgZXN0aW1hdGluZyBhcHByb3ggMmhycyBvZiBydW50aW1lIGZvciBhbGwgMi44bSByZWNvcmRzClRlc3RpbmdfQSA8LSBzcWxkZigiICAgU2VsZWN0ICAgICAgICAgICAgICAgdDEuKgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICx0Mi5FdmVudF9UaW1lICAgICAgICAgICAgIGFzIE5leHRCdXMKICAgICAgICAgICAgICAgICAgICAgICAgRnJvbSAgICAgICAgICAgICAgICAgU2FtcCAgICAgICAgICAgICAgICAgICAgICBhcyB0MQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIElubmVyIEpvaW4gICAgICBTYW1wICAgICAgICAgICAgICAgICAgICAgIGFzIHQyCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgT24gICAgICAgICAgICAgIHQxLlJvdXRlID0gdDIuUm91dGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBbmQgICAgICAgICAgICAgdDEuU3RvcElEX0NsZWFuID0gdDIuU3RvcElEX0NsZWFuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQW5kICAgICAgICAgICAgIHQyLkV2ZW50X1RpbWUgPiB0MS5TYW1wVGltZQogICAgICAgICAgICAgICAgICAgICAgICBPcmRlciBCeSAgICAgICAgICAgICB0MS5Sb3V0ZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICx0MS5TdG9wSURfQ2xlYW4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsdDEuRXZlbnRfVGltZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICx0Mi5FdmVudF9UaW1lCiAgICAgICAgICAgICAgICAgICIKICAgICAgICAgICAgICAgICApICU+JSAKICBtdXRhdGUoTkIgPSBhc19kYXRldGltZShOZXh0QnVzLAogICAgICAgICAgICAgICAgICAgICAgICAgIHR6ID0gIkFtZXJpY2EvTmV3X1lvcmsiCiAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKQoKIyBzdHIoVGVzdGluZ19BKQojIFZpZXcoaGVhZChUZXN0aW5nX0EsIDUwMCkpCiMgVmlldyhoZWFkKFNhbXAsIDUwMCkpCgoKIyBGaWx0ZXIgdGhlIGRhdGFmcmFtZSB0byBvbmx5IGluY2x1ZGUgdGhlIGJ1cyBhcnJpdmFsIGF0IFN0b3BJRCB0aGF0IGlzIHRoZSBuZXh0IHRvIGNvbWUgYWZ0ZXIgdGhlIFNhbXBUaW1lLgojIGVzdGltYXRpbmcgYXBwcm94IDIwbWluIG9mIHJ1bnRpbWUgZm9yIGFsbCAyLjhtIHJlY29yZHMKVGVzdGluZyA8LSBzZWxlY3QoVGVzdGluZ19BLAogICAgICAgICAgICAgICAgICAtTmV4dEJ1cwogICAgICAgICAgICAgICAgICkgJT4lIAogIGdyb3VwX2J5KFJvd051bV9PRykgJT4lIAogIGZpbHRlcihOQiA9PSBtaW4oTkIpCiAgICAgICAgKSAlPiUgCiAgYXJyYW5nZShSb3V0ZSwKICAgICAgICAgIFN0b3BJRF9DbGVhbiwKICAgICAgICAgIEV2ZW50X1RpbWUKICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFdhaXRUaW1lX01pbiA9IGFzLm51bWVyaWMoTkIgLSBTYW1wVGltZSksCiAgICAgICAgIFdhaXRUaW1lX1NlYyA9IFdhaXRUaW1lX01pbiAqIDYwLAogICAgICAgICBXYWl0VGltZV9TZWMyID0gTkIgLSBTYW1wVGltZSwKICAgICAgICAgV2FpdFRpbWVfTWluMiA9IFdhaXRUaW1lX1NlYzIgLyA2MAogICAgICAgICkgJT4lIAogIGFzLmRhdGEuZnJhbWUoKQoKYXNzaWduKHBhc3RlMCgiVGVzdGluZ18iLCBpKSwKICAgICAgIFRlc3RpbmcKICAgICAgKQoKcm0oU2FtcCxUZXN0aW5nX0EsIFRlc3RpbmcpCnN0cihnZXQocGFzdGUwKCJUZXN0aW5nXyIsIGkpKSkKVmlldyhnZXQocGFzdGUwKCJUZXN0aW5nXyIsIGkpKSkKfQoKCiMgQmluZCBhbGwgdGhlIGluZGl2aWR1YWwgZGF0YWZyYW1lcyB0b2dldGhlci4KV2FpdERhdGFfRGF5UHVsbCA8LSBiaW5kX3Jvd3MoVGVzdGluZ18zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUZXN0aW5nXzQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRlc3RpbmdfNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGVzdGluZ182LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUZXN0aW5nXzcKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBtdXRhdGUoV2FpdFRpbWVfU2VjMyA9IE5CIC0gU2FtcFRpbWUsCiAgICAgICAgIFdhaXRUaW1lX01pbjMgPSBXYWl0VGltZV9TZWMzIC8gNjAKICAgICAgICApICU+JSAKICBhcnJhbmdlKFJvdXRlLAogICAgICAgICAgU3RvcElEX0NsZWFuLAogICAgICAgICAgRXZlbnRfVGltZQogICAgICAgICApCgpybShUZXN0aW5nXzMsIFRlc3RpbmdfNCwgVGVzdGluZ181LCBUZXN0aW5nXzYsIFRlc3RpbmdfNykKc3RyKFdhaXREYXRhX0RheVB1bGwpClZpZXcoaGVhZChXYWl0RGF0YV9EYXlQdWxsLCA1MDApKQpWaWV3KHRhaWwoV2FpdERhdGFfRGF5UHVsbCwgNTAwKSkKCmBgYAoKCldhaXRpbmcgdGltZSBhbmFseXNlcy4KCk11bmdpbmcgYW5kIHNhbXBsaW5nIGRhdGEgdG8gZ28gZnJvbSB0aW1lIGJldGVlbiBidXNlcyB0byAiYXZlcmFnZSIgd2FpdGluZyB0aW1lLgoKQmFzaWMgaW52ZXN0aWdhdGlvbiBvZiBhbnkgbWlzc2luZyByb3dzIGZyb20gZGF0YSBwdWxsZWQgYnkgZGF5LgpgYGB7cn0KCkRpc3RpbmN0Um93TnVtX09HIDwtIGRpc3RpbmN0KHNlbGVjdChXYWl0RGF0YV9EYXlQdWxsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUm93TnVtX09HCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgpzdHIoRGlzdGluY3RSb3dOdW1fT0cpCgojIFZpZXcoCiMgYW50aV9qb2luKFNhbXAsCiMgICAgICAgICAgIERpc3RpbmN0Um93TnVtX09HLAojICAgICAgICAgICBieSA9IGMoIlJvd051bV9PRyIgPSAiUm93TnVtX09HIikKIyAgICAgICAgICApCiMgKQoKCiMgVGhlIHNhbXAgdGltZSBpcyBBRlRFUiB0aGUgbGFzdCBidXMgcGFzc2VkIHRoYXQgU3RvcElEX0NsZWFuCiMgVmlldyhmaWx0ZXIoU2FtcCwKIyAgICAgICAgICAgICBFdmVudF9UaW1lID4gIjIwMTYtMTAtMDcgMTk6NDg6NDEiICYKIyAgICAgICAgICAgICAgIFJvdXRlID09ICJYMiIgJgojICAgICAgICAgICAgICAgU3RvcElEX0NsZWFuID09IDEwMDM3NzQKIyAgICAgICAgICAgICkKIyAgICAgKQoKIyBOZXh0IEJ1cyAoTkIpIGNhbiBiZSBvbiB0aGUgbmV4dCBtb3JuaW5nCiMgVmlldyhmaWx0ZXIoVGVzdGluZzcsCiMgICAgICAgICAgICAgU2FtcFRpbWUgPiAiMjAxNi0xMC0wNiAyMzo1ODowMCIgJgojICAgICAgICAgICAgICAgU2FtcFRpbWUgPCAiMjAxNi0xMC0wNiAyMzo1OTo1OSIpCiMgICAgICkKCmBgYAoKCldhaXRpbmcgdGltZSBhbmFseXNlcy4KCk11bmdpbmcgYW5kIHNhbXBsaW5nIGRhdGEgdG8gZ28gZnJvbSB0aW1lIGJldGVlbiBidXNlcyB0byAiYXZlcmFnZSIgd2FpdGluZyB0aW1lLgoKKFB1bGxzIGhlcmUgYXJlIGRvbmUgYnkgZ3JvdXBpbmdzIG9mIGJ1cyByb3V0ZXMsIGFzIHRoZSBkYXRhIGFyZSB0b28gbGFyZ2UgdG8gZG8gYXQgb25jZS4pCgpGaXJzdCwgd2UgbmVlZCB0byBmaW5kIHRoZSBtb3N0IGNvbW1vbiBidXMgcm91dGVzLgpgYGB7cn0KCnJtKERpc3RpbmN0Um93TnVtX09HKQoKCiMgVmlldyhoZWFkKE5ld1RyYXZUaW1lLCA1MDApKQoKc2V0LnNlZWQoMTIzNDU2Nzg5KQpCdXNHcm91cHMgPC0gZ3JvdXBfYnkoTmV3VHJhdlRpbWUsCiAgICAgICAgICAgICAgICAgICAgICBSb3V0ZQogICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzdW1tYXJpc2UoQ250X051bSA9IG4oKSwKICAgICAgICAgICAgQ250X1BjdCA9IENudF9OdW0gLyBucm93KE5ld1RyYXZUaW1lKQogICAgICAgICAgICkgJT4lIAogIGFycmFuZ2UoZGVzYyhDbnRfTnVtKQogICAgICAgICApICU+JSAKICBtdXRhdGUoUm93TnVtID0gcm93X251bWJlcigpLAogICAgICAgICBSYW5kTnVtID0gcnVuaWYobiA9IDI2OCksCiAgICAgICAgIFJvdXRlR3JvdXAgPSBpZmVsc2UoUmFuZE51bSA8PSAwLjIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMSwKICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShSYW5kTnVtIDw9IDAuNCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAyLAogICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFJhbmROdW0gPD0gMC42LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIDMsCiAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoUmFuZE51bSA8PSAwLjgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgNCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICA1CiAgICAgICAgICAgICAgICAgICAgICAgICAgICApKSkpCiAgICAgICAgKQoKc3RyKEJ1c0dyb3VwcykKVmlldyhCdXNHcm91cHMpCnN1bW1hcnkoQnVzR3JvdXBzKQoKYGBgCgoKV2FpdGluZyB0aW1lIGFuYWx5c2VzLgoKTXVuZ2luZyBhbmQgc2FtcGxpbmcgZGF0YSB0byBnbyBmcm9tIHRpbWUgYmV0ZWVuIGJ1c2VzIHRvICJhdmVyYWdlIiB3YWl0aW5nIHRpbWUuCgooUHVsbHMgaGVyZSBhcmUgZG9uZSBieSBncm91cGluZ3Mgb2YgYnVzIHJvdXRlcywgYXMgdGhlIGRhdGEgYXJlIHRvbyBsYXJnZSB0byBkbyBhdCBvbmNlLikKYGBge3J9CgojIFZpZXcoaGVhZChOZXdUcmF2VGltZSwgNTAwKSkKCiMgRm9yIGVhY2ggcmVjb3JkLCBjcmVhdGUgYSByYW5kb20gZGF0ZXRpbWUgYmV0d2VlbiB0aGUgZmlyc3QgYW5kIGxhc3Qgc3RvcCBmb3IgdGhhdCBidXMgcm91dGUgKG9uIHRoYXQgZGF5KS4KZm9yKGkgaW4gMTo1KXsKICAKc2V0LnNlZWQoMTIzNDU2Nzg5KQpTYW1wIDwtIGxlZnRfam9pbihOZXdUcmF2VGltZSwKICAgICAgICAgICAgICAgICAgQnVzR3JvdXBzLAogICAgICAgICAgICAgICAgICBieSA9IGMoIlJvdXRlIiA9ICJSb3V0ZSIpCiAgICAgICAgICAgICAgICAgICkgJT4lIAogIHNlbGVjdChSb3dOdW1fT0csCiAgICAgICAgIFJvdXRlLAogICAgICAgICBSb3V0ZUdyb3VwLAogICAgICAgICBFdmVudF9UaW1lX0RhdGUsCiAgICAgICAgIFN0b3BJRF9DbGVhbiwKICAgICAgICAgc3RhcnRzX3dpdGgoIkV2ZW50IikKICAgICAgICApICU+JSAKICBmaWx0ZXIoUm91dGVHcm91cCA9PSBpKSAlPiUgICMgbmVlZGVkIHRvIGRvIHRoaXMgZWFjaCBSb3V0ZUdyb3VwICgxLTUpIGJlY2F1c2UgdGhlIGNvbXBsZXRlIGZpbGUgd2FzIHRvbyBsYXJnZSB0byBkbyBhdCBvbmNlCiAgbGVmdF9qb2luKFJvdXRlTWluTWF4LAogICAgICAgICAgICBieSA9IGMoIlJvdXRlIiA9ICJSb3V0ZSIsCiAgICAgICAgICAgICAgICAgICAiRXZlbnRfVGltZV9EYXRlIiA9ICJFdmVudF9UaW1lX0RhdGUiCiAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICApICU+JSAKICBtdXRhdGUoU2FtcFRpbWUgPSBhc19kYXRldGltZShydW5pZihucm93KC4pLCAjMjAwMDAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbiA9IE1pblRpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4ID0gTWF4VGltZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eiA9ICJBbWVyaWNhL05ld19Zb3JrIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICkgJT4lIAogIGFycmFuZ2UoUm91dGUsCiAgICAgICAgICBTdG9wSURfQ2xlYW4sCiAgICAgICAgICBFdmVudF9UaW1lCiAgICAgICAgICkgCgojIHN0cihTYW1wKQojIFZpZXcoaGVhZChTYW1wLCA1MDApKQojIAojIFZpZXcoCiMgZ3JvdXBfYnkoU2FtcCwKIyAgICAgICAgICBSb3dOdW1fT0cKIyAgICAgICAgICkgJT4lCiMgICBzdW1tYXJpc2UoQ250X051bSA9IG4oKSwKIyAgICAgICAgICAgICBDbnRfUGN0ID0gMTAwICogQ250X051bSAvIG5yb3coU2FtcCkKIyAgICAgICAgICAgICkgJT4lCiMgICBhcnJhbmdlKGRlc2MoQ250X051bSkpCiMgKQoKCiMgRm9yIGVhY2ggUm91dGUgYW5kIFN0b3BJRCBjb21iaW5hdGlvbiwgZ2V0IGFsbCB0aGUgRXZlbnRfVGltZSB2YWx1ZXMgdGhhdCBhcmUgYWZ0ZXIgdGhlIFNhbXBUaW1lIHZhbHVlLgojIGVzdGltYXRpbmcgYXBwcm94IDJocnMgb2YgcnVudGltZSBmb3IgYWxsIDIuOG0gcmVjb3JkcwpUZXN0aW5nX0EgPC0gc3FsZGYoIiAgIFNlbGVjdCAgICAgICAgICAgICAgIHQxLioKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsdDIuRXZlbnRfVGltZSAgICAgICAgICAgICBhcyBOZXh0QnVzCiAgICAgICAgICAgICAgICAgICAgICAgIEZyb20gICAgICAgICAgICAgICAgIFNhbXAgICAgICAgICAgICAgICAgICAgICAgYXMgdDEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbm5lciBKb2luICAgICAgU2FtcCAgICAgICAgICAgICAgICAgICAgICBhcyB0MgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE9uICAgICAgICAgICAgICB0MS5Sb3V0ZSA9IHQyLlJvdXRlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQW5kICAgICAgICAgICAgIHQxLlN0b3BJRF9DbGVhbiA9IHQyLlN0b3BJRF9DbGVhbgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFuZCAgICAgICAgICAgICB0Mi5FdmVudF9UaW1lID4gdDEuU2FtcFRpbWUKICAgICAgICAgICAgICAgICAgICAgICAgT3JkZXIgQnkgICAgICAgICAgICAgdDEuUm91dGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsdDEuU3RvcElEX0NsZWFuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLHQxLkV2ZW50X1RpbWUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsdDIuRXZlbnRfVGltZQogICAgICAgICAgICAgICAgICAiCiAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKE5CID0gYXNfZGF0ZXRpbWUoTmV4dEJ1cywKICAgICAgICAgICAgICAgICAgICAgICAgICB0eiA9ICJBbWVyaWNhL05ld19Zb3JrIgogICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICkKCiMgc3RyKFRlc3RpbmdfQSkKIyBWaWV3KGhlYWQoVGVzdGluZ19BLCA1MDApKQojIFZpZXcoaGVhZChTYW1wLCA1MDApKQoKCiMgRmlsdGVyIHRoZSBkYXRhZnJhbWUgdG8gb25seSBpbmNsdWRlIHRoZSBidXMgYXJyaXZhbCBhdCBTdG9wSUQgdGhhdCBpcyB0aGUgbmV4dCB0byBjb21lIGFmdGVyIHRoZSBTYW1wVGltZS4KIyBlc3RpbWF0aW5nIGFwcHJveCAyMG1pbiBvZiBydW50aW1lIGZvciBhbGwgMi44bSByZWNvcmRzClRlc3RpbmcgPC0gc2VsZWN0KFRlc3RpbmdfQSwKICAgICAgICAgICAgICAgICAgLU5leHRCdXMKICAgICAgICAgICAgICAgICApICU+JSAKICBncm91cF9ieShSb3dOdW1fT0cpICU+JSAKICBmaWx0ZXIoTkIgPT0gbWluKE5CKQogICAgICAgICkgJT4lIAogIGFycmFuZ2UoUm91dGUsCiAgICAgICAgICBTdG9wSURfQ2xlYW4sCiAgICAgICAgICBFdmVudF9UaW1lCiAgICAgICAgICkgJT4lIAogIG11dGF0ZShXYWl0VGltZV9NaW4gPSBhcy5udW1lcmljKE5CIC0gU2FtcFRpbWUpLAogICAgICAgICBXYWl0VGltZV9TZWMgPSBXYWl0VGltZV9NaW4gKiA2MAogICAgICAgICkgJT4lIAogIGFzLmRhdGEuZnJhbWUoKQoKYXNzaWduKHBhc3RlMCgiVGVzdGluZyIsIGkpLAogICAgICAgVGVzdGluZwogICAgICApCgpybShTYW1wLFRlc3RpbmdfQSwgVGVzdGluZykKc3RyKGdldChwYXN0ZTAoIlRlc3RpbmciLCBpKSkpClZpZXcoZ2V0KHBhc3RlMCgiVGVzdGluZyIsIGkpKSkKfQoKCiMgQmluZCBhbGwgdGhlIGluZGl2aWR1YWwgZGF0YWZyYW1lcyB0b2dldGhlci4KV2FpdERhdGFfUm91dGVQdWxsIDwtIGJpbmRfcm93cyhUZXN0aW5nMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUZXN0aW5nMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUZXN0aW5nMywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUZXN0aW5nNCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUZXN0aW5nNQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIG11dGF0ZShXYWl0VGltZV9TZWMyID0gTkIgLSBTYW1wVGltZSwKICAgICAgICAgV2FpdFRpbWVfTWluMiA9IFdhaXRUaW1lX1NlYzIgLyA2MAogICAgICAgICkgJT4lIAogIGFycmFuZ2UoUm91dGUsCiAgICAgICAgICBTdG9wSURfQ2xlYW4sCiAgICAgICAgICBFdmVudF9UaW1lCiAgICAgICAgICkKCnJtKEJ1c0dyb3VwcywgaSwgVGVzdGluZzMsIFRlc3Rpbmc0LCBUZXN0aW5nNSwgVGVzdGluZzYsIFRlc3Rpbmc3KQpzdHIoV2FpdERhdGFfUm91dGVQdWxsKQpWaWV3KGhlYWQoV2FpdERhdGFfUm91dGVQdWxsLCA1MDApKQpWaWV3KHRhaWwoV2FpdERhdGFfUm91dGVQdWxsLCA1MDApKQoKYGBgCgoKV2FpdGluZyB0aW1lIGFuYWx5c2VzLgoKTXVuZ2luZyBhbmQgc2FtcGxpbmcgZGF0YSB0byBnbyBmcm9tIHRpbWUgYmV0ZWVuIGJ1c2VzIHRvICJhdmVyYWdlIiB3YWl0aW5nIHRpbWUuCgpDb21wYXJlIFdhaXREYXRhIHB1bGxlZCBieSBkYXkgYW5kIHB1bGxlZCBieSByb3V0ZS4KYGBge3J9CgpkaW0oV2FpdERhdGFfUm91dGVQdWxsKQpkaW0oV2FpdERhdGFfRGF5UHVsbCkKbnJvdyhXYWl0RGF0YV9Sb3V0ZVB1bGwpIC0gbnJvdyhXYWl0RGF0YV9EYXlQdWxsKQoKV2FpdERhdGFfRGlmZiA8LSBhbnRpX2pvaW4oV2FpdERhdGFfUm91dGVQdWxsLAogICAgICAgICAgICAgICAgICAgICAgICAgICBXYWl0RGF0YV9EYXlQdWxsLAogICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IGMoIlJvd051bV9PRyIgPSAiUm93TnVtX09HIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc2VsZWN0KC1XYWl0VGltZV9NaW4sCiAgICAgICAgIC1XYWl0VGltZV9TZWMKICAgICAgICApCgpzdHIoV2FpdERhdGFfRGlmZikKVmlldyhoZWFkKFdhaXREYXRhX0RpZmYsIDUwMCkpCgpWaWV3KGZpbHRlcihXYWl0RGF0YV9Sb3V0ZVB1bGwsCiAgICAgICAgICAgIFJvdXRlID09ICJaOCIgJgogICAgICAgICAgICAgIFN0b3BJRF9DbGVhbiA9PSAyMDA1NDY1CiAgICAgICAgICAgICMgUm93TnVtX09HID0gMjkwMjc2MAogICAgICAgICAgICAjIEV2ZW50X1RpbWUgPSAyMDE2LTEwLTA3IDE5OjUxOjQ3CiAgICAgICAgICAgKQogICAgKQoKVmlldyhncm91cF9ieShXYWl0RGF0YV9EaWZmLAogICAgICAgICAgICAgIFJvdXRlCiAgICAgICAgICAgICApICU+JSAKICAgICAgIHN1bW1hcmlzZShDbnRfTnVtID0gbigpLAogICAgICAgICAgICAgICAgIENudF9QY3QgPSBDbnRfTnVtIC8gbnJvdyhXYWl0RGF0YV9EaWZmKQogICAgICAgICAgICAgICAgKSAlPiUgCiAgICAgICBhcnJhbmdlKGRlc2MoQ250X051bSkKICAgICAgICAgICAgICApCiAgICApCgpWaWV3KGZpbHRlcihXYWl0RGF0YV9EaWZmLAogICAgICAgICAgICBSb3V0ZSA9PSAiUzEiCiAgICAgICAgICAgKQogICAgKQoKVmlldyhmaWx0ZXIoV2FpdERhdGFfUm91dGVQdWxsLAogICAgICAgICAgICBSb3V0ZSA9PSAiUzEiICYKICAgICAgICAgICAgICBTdG9wSURfQ2xlYW4gPT0gMTAwMzEzMgogICAgICAgICAgICAjIFJvd051bV9PRyA9IDExNTE3NzAKICAgICAgICAgICAgIyBFdmVudF9UaW1lID0gMjAxNi0xMC0wNyAwOTowNzoxMgogICAgICAgICAgICkKICAgICkKCiMgQ2FuJ3QgdGVsbCB3aHkgdGhlIHB1bGwgYnkgZGF5IGhhcyBsZXNzIHJlY29yZHMgdGhhbiB0aGUgcHVsbCBieSByb3V0ZQoKYGBgCgoKV2FpdGluZyB0aW1lIGFuYWx5c2VzLgoKTXVuZ2luZyBhbmQgc2FtcGxpbmcgZGF0YSB0byBnbyBmcm9tIHRpbWUgYmV0ZWVuIGJ1c2VzIHRvICJhdmVyYWdlIiB3YWl0aW5nIHRpbWUuCgpDb21wYXJlIFdhaXREYXRhIChwdWxsZWQgYnkgcm91dGUpIGFuZCBvcmlnaW5hbCBkYXRhIChOZXdUcmF2VGltZSkuCmBgYHtyfQoKZGltKE5ld1RyYXZUaW1lKSAgIyAyLDgwOSw1Mjkgcm93cwpkaW0oV2FpdERhdGFfUm91dGVQdWxsKSAgIyAyLDc4MCw4NDggcm93cwpucm93KE5ld1RyYXZUaW1lKSAtIG5yb3coV2FpdERhdGFfUm91dGVQdWxsKSAgIyBpcyAyOCw2ODEgcm93cwoKc3RyKHNlbGVjdChOZXdUcmF2VGltZSwKICAgICAgICAgICAtbWF0Y2hlcygiKHEoMnw1fCg5NSl8KDk4KSkpfE1lYW58TWVkfENudCIpCiAgICAgICAgICApCiAgICkKc3RyKFdhaXREYXRhX1JvdXRlUHVsbCkKCkNvbXBhcmVfTlRUX1dEIDwtIGxlZnRfam9pbihOZXdUcmF2VGltZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdChXYWl0RGF0YV9Sb3V0ZVB1bGwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUm93TnVtX09HLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgUm91dGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUm91dGVHcm91cCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFN0b3BJRF9DbGVhbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEV2ZW50X1RpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTWluVGltZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNYXhUaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNhbXBUaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5CLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFdhaXRUaW1lX1NlYzIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV2FpdFRpbWVfTWluMgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygiUm93TnVtX09HIiA9ICJSb3dOdW1fT0ciKQogICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzZWxlY3QoLW1hdGNoZXMoIihxKDJ8NXwoOTUpfCg5OCkpKXxNZWFufE1lZHxDbnQiKQogICAgICAgICkgJT4lIAogIGFycmFuZ2UoUm91dGUsCiAgICAgICAgICBTdG9wSURfQ2xlYW4sCiAgICAgICAgICBFdmVudF9UaW1lCiAgICAgICAgICkKCnN0cihDb21wYXJlX05UVF9XRCkgICMgMiw4MTAsMTA5IHJvd3Mgb3ZlcmFsbCAgLS0gIDI5LDI2MSByb3dzIHdpdGggbm8gbWF0Y2gKVmlldyhoZWFkKENvbXBhcmVfTlRUX1dELCA1MDApKQpWaWV3KGZpbHRlcihDb21wYXJlX05UVF9XRCwKICAgICAgICAgICAgaXMubmEoTWluVGltZSkKICAgICAgICAgICApCiAgICApCgoKCiMgVmlldyhhbnRpX2pvaW4oU2FtcCwKIyAgICAgICAgICAgICAgICBkaXN0aW5jdChzZWxlY3QoV2FpdERhdGFfUm91dGVQdWxsLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSb3dOdW1fT0cKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiMgICAgICAgICAgICAgICAgICAgICAgICApLAojICAgICAgICAgICAgICAgIGJ5ID0gYygiUm93TnVtX09HIiA9ICJSb3dOdW1fT0ciKQojICAgICAgICAgICAgICAgKQojICAgICApCgojIFRoZSBTYW1wVGltZSBpcyBBRlRFUiB0aGUgbGFzdCBidXMgcGFzc2VkIHRoYXQgU3RvcElEX0NsZWFuCiMgVmlldyhmaWx0ZXIoU2FtcCwKIyAgICAgICAgICAgICAgIFJvdXRlID09ICJYMiIgJgojICAgICAgICAgICAgICAgU3RvcElEX0NsZWFuID09IDEwMDM3NzQKIyAgICAgICAgICAgICAjIFJvd051bV9PRyA9IDExNDY3MjMKIyAgICAgICAgICAgICAjIEV2ZW50X1RpbWUgPSAyMDE2LTEwLTA3IDE1OjMyOjE4CiMgICAgICAgICAgICApCiMgICAgICkKCmBgYAoKCkNsZWFuIHVwIHRoZSBkYXRhIGEgYml0LgpgYGB7cn0KCnJtKEJ1c0dyb3VwcywgUm91dGVNaW5NYXgsIFNhbXAsIFRlc3RpbmcxLCBUZXN0aW5nMiwgVGVzdGluZzMsIFRlc3Rpbmc0LCBUZXN0aW5nNSwgVGVzdGluZ18zLCBUZXN0aW5nXzQsIFRlc3RpbmdfNSwgVGVzdGluZ182LCBUZXN0aW5nXzcsIFdhaXREYXRhX0RheVB1bGwsIFdhaXREYXRhX0RpZmYpCgoKc3RyKENvbXBhcmVfTlRUX1dEKQpWaWV3KGhlYWQoQ29tcGFyZV9OVFRfV0QsIDUwMCkpClZpZXcoaGVhZChtdXRhdGUoQ29tcGFyZV9OVFRfV0QsCiAgICAgICAgICAgICAgICAgV1RfTWluID0gYXMubnVtZXJpYyhXYWl0VGltZV9NaW4yKQogICAgICAgICAgICAgICAgKQogICAgICAgICApCiAgICApCgpXYWl0VGltZV9Bc051bSA8LSBDb21wYXJlX05UVF9XRCAlPiUgCiAgbXV0YXRlKFJvdXRlU3RvcF9JRCA9IGZhY3RvcihwYXN0ZShSb3V0ZSwgU3RvcElEX0NsZWFuLCBzZXAgPSAiX18iKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKQpXYWl0VGltZV9Bc051bSRXYWl0VGltZV9TZWMyIDwtIGFzLm51bWVyaWMoV2FpdFRpbWVfQXNOdW0kV2FpdFRpbWVfU2VjMikKV2FpdFRpbWVfQXNOdW0kV2FpdFRpbWVfTWluMiA8LSBhcy5udW1lcmljKFdhaXRUaW1lX0FzTnVtJFdhaXRUaW1lX01pbjIpCgpybShDb21wYXJlX05UVF9XRCkKc3RyKFdhaXRUaW1lX0FzTnVtKQoKYGBgCgoKR2VuZXJhbCBleHBsb3JhdGlvbiBvZiB3YWl0IHRpbWVzLgpgYGB7cn0KCnN1bW1hcnkoV2FpdFRpbWVfQXNOdW0kV2FpdFRpbWVfTWluMikKCmBgYAoKCkdlbmVyYWwgZXhwbG9yYXRpb24gb2Ygd2FpdCB0aW1lcy4KYGBge3J9CgpXVF9RdWFudGlsZXMgPC0gYXMuZGF0YS5mcmFtZShxdWFudGlsZShXYWl0VGltZV9Bc051bSRXYWl0VGltZV9NaW4yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9icyA9IHNlcSgwLCAxLCAwLjAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCmNvbG5hbWVzKFdUX1F1YW50aWxlcykgPC0gIlZhbHVlX01pbiIKCldUX1F1YW50aWxlcyRWYWx1ZV9TZWMgPSBmb3JtYXQocm91bmQoV1RfUXVhbnRpbGVzJFZhbHVlX01pbiAqIDYwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpZ2l0cyA9IDIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnNtYWxsID0gMgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQpXVF9RdWFudGlsZXMkVmFsdWVfSHIgPSBmb3JtYXQocm91bmQoV1RfUXVhbnRpbGVzJFZhbHVlX01pbiAvIDYwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlnaXRzID0gMgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zbWFsbCA9IDIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKV1RfUXVhbnRpbGVzJFZhbHVlX01pbiA9IGZvcm1hdChyb3VuZChXVF9RdWFudGlsZXMkVmFsdWVfTWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpZ2l0cyA9IDIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnNtYWxsID0gMgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKV1RfUXVhbnRpbGVzJFF1YW50aWxlIDwtIHNlcSgwLCAxLCAwLjAxKQoKV1RfUXVhbnRpbGVzIDwtIHNlbGVjdChXVF9RdWFudGlsZXMsCiAgICAgICAgICAgICAgICAgICAgICAgUXVhbnRpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgVmFsdWVfU2VjLAogICAgICAgICAgICAgICAgICAgICAgIFZhbHVlX01pbiwKICAgICAgICAgICAgICAgICAgICAgICBWYWx1ZV9IcgogICAgICAgICAgICAgICAgICAgICAgKQoKc3RyKFdUX1F1YW50aWxlcykKVmlldyhXVF9RdWFudGlsZXMpCldUX1F1YW50aWxlcwoKClZpZXcoYXJyYW5nZShXYWl0VGltZV9Bc051bSwKICAgICAgICAgICAgIGRlc2MoV2FpdFRpbWVfTWluMikKICAgICAgICAgICAgKSAlPiUgCiAgICAgICBoZWFkKC4sIDUwMDApCiAgICApCgpWaWV3KGZpbHRlcihXYWl0VGltZV9Bc051bSwKICAgICAgICAgICAgYmV0d2VlbihXYWl0VGltZV9NaW4yLCA2MCwgMjAwKQogICAgICAgICAgICkgJT4lIAogICAgICAgYXJyYW5nZShkZXNjKFdhaXRUaW1lX01pbjIpCiAgICAgICAgICAgICAgKSAKICAgICAjICU+JSAKICAgICAjICAgaGVhZCguLCA1MDAwKQogICAgKQoKIyBFeGFtcGxlIG9mIGV4dHJlbWUgd2FpdCB0aW1lcwpWaWV3KGZpbHRlcihXYWl0VGltZV9Bc051bSwKICAgICAgICAgICAgUm91dGUgPT0gIlcxMyIgJiAgIyBvbmx5IDIgYnVzIHBhc3NlcyBpbiB0aGUgZW50aXJlIGRhdGFzZXQKICAgICAgICAgICAgICBTdG9wSURfQ2xlYW4gPT0gMTAwMzcyOAogICAgICAgICAgICAjIEV2ZW50X1RpbWUgPSAyMDE2LTEwLTAzIDA4OjQyOjQ2CiAgICAgICAgICAgKQogICAgKQoKIyBFeGFtcGxlIG9mIGV4dHJlbWUgd2FpdCB0aW1lcwpWaWV3KGZpbHRlcihXYWl0VGltZV9Bc051bSwKICAgICAgICAgICAgUm91dGUgPT0gIlM0MSIgJiAgIyBvbmx5IDQgYnVzIHBhc3NlcyBpbiB0aGUgZW50aXJlIGRhdGFzZXQKICAgICAgICAgICAgICBTdG9wSURfQ2xlYW4gPT0gMTAwMTA5NQogICAgICAgICAgICAjIEV2ZW50X1RpbWUgPSAyMDE2LTEwLTA1IDE1OjQxOjQ3CiAgICAgICAgICAgKQogICAgKQoKIyBFeGFtcGxlIG9mIGV4dHJlbWUgd2FpdCB0aW1lcwpWaWV3KGZpbHRlcihXYWl0VGltZV9Bc051bSwKICAgICAgICAgICAgUm91dGUgPT0gIkQ4IiAmICAjIHJvdXRlIGhhcyBWRVJZIGxpbWl0ZWQgc2VydmljZSBhZnRlciBtaWRuaWdodAogICAgICAgICAgICAgIFN0b3BJRF9DbGVhbiA9PSAxMDAxNjY5CiAgICAgICAgICAgICMgRXZlbnRfVGltZSA9IDIwMTYtMTAtMDYgMjA6MzE6MTYKICAgICAgICAgICApCiAgICApCgpgYGAKCgpMb29rcyBsaWtlIHRoZXJlIG1pZ2h0IGJlIGFuIGlzc3VlIGluIHdhaXQgdGltZXMgd2hlbiB2ZXJ5IGZldyBSb3V0ZS1TdG9wIGNvbWJpbmF0aW9ucyBhcmUgaW5jbHVkZWQgaW4gdGhlIGRhdGFzZXQuICBMZXQncyBleHBsb3JlIHRoZXNlLgpgYGB7cn0KClJvdXRlU3RvcF9DbnRzIDwtIGdyb3VwX2J5KFdhaXRUaW1lX0FzTnVtLAogICAgICAgICAgICAgICAgICAgICAgICAgICBSb3V0ZVN0b3BfSUQKICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzdW1tYXJpc2UoUm91dGVTdG9wX0NudE51bSA9IG4oKSwKICAgICAgICAgICAgUm91dGVTdG9wX0NudFBjdCA9IFJvdXRlU3RvcF9DbnROdW0gLyBucm93KFdhaXRUaW1lX0FzTnVtKQogICAgICAgICAgICkgJT4lIAogIGFycmFuZ2UoUm91dGVTdG9wX0NudE51bSkKClZpZXcoUm91dGVTdG9wX0NudHMpCgoKUm91dGVTdG9wX0NudE9mQ250IDwtIGdyb3VwX2J5KFJvdXRlU3RvcF9DbnRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUm91dGVTdG9wX0NudE51bQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzdW1tYXJpc2UoUm91dGVTdG9wQ250X0NudE51bSA9IG4oKSwKICAgICAgICAgICAgUm91dGVTdG9wQ250X0NudFBjdCA9IFJvdXRlU3RvcENudF9DbnROdW0gLyBucm93KFJvdXRlU3RvcF9DbnRzKQogICAgICAgICAgICkgJT4lIAogIG11dGF0ZShSb3V0ZVN0b3BDbnRfQ250UGN0X0N1bVN1bSA9IGN1bXN1bShSb3V0ZVN0b3BDbnRfQ250UGN0KSwKICAgICAgICAgeCA9IDEgLSBSb3V0ZVN0b3BDbnRfQ250UGN0X0N1bVN1bQogICAgICAgICkgJT4lIAogIGFycmFuZ2UoUm91dGVTdG9wX0NudE51bSkKICAKIFZpZXcoUm91dGVTdG9wX0NudE9mQ250KQogUm91dGVTdG9wX0NudE9mQ250CgpgYGAKCgpIaXN0b2dyYW0gb2YgdGhlIGNvdW50cyBvZiBSb3V0ZS1TdG9wSUQgY29tYmluYXRpb25zLgpgYGB7cn0KClJvdXRlU3RvcF9DbnRzX0JhciA8LSBnZ3Bsb3QoUm91dGVTdG9wX0NudE9mQ250LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gUm91dGVTdG9wX0NudE51bSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB5ID0gLi5kZW5zaXR5Li4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IFJvdXRlU3RvcENudF9DbnROdW0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsKICAjIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNSwgZmlsbCA9ICJsaWdodGJsdWUiLCBjb2xvdXIgPSAiZ3JleTYwIiwgc2l6ZSA9IDAuMikgKwogIGdlb21fY29sKGZpbGwgPSAibGlnaHRibHVlIiwgY29sb3VyID0gImdyZXk2MCIsIHNpemUgPSAwLjIpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwgNTAwKQogICAgICAgICAgICAgICAgICAjIHlsaW0gPSBjKDAsIDAuMDIpCiAgICAgICAgICAgICAgICAgKSArCiAgbGFicyh0aXRsZSA9ICJWYXJpYXRpb24gaW4gUm91dGVzIFBhc3NpbmcgYSBTcGVjaWZpYyBTdG9wIiwKICAgICAgIHggPSAiT2NjdXJyZW5jZXMgb2YgUm91dGUtU3RvcElEIENvbWJpYW50aW9ucyIsCiAgICAgICB5ID0gIkNvdW50cyIKICAgICAgKQoKUm91dGVTdG9wX0NudHNfQmFyCgpgYGAKCgpDcmVhdGUgYSBuZXcgZGF0YXNldCBsaW1pdGluZyBleHRyZW1lbHkgc21hbGwgY291bnRzIG9mIFJvdXRlLVN0b3BJRCBjb21iaW5hdGlvbnMuCmBgYHtyfQoKV2FpdFRpbWVfUnRlQ250cyA8LSBsZWZ0X2pvaW4oV2FpdFRpbWVfQXNOdW0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJvdXRlU3RvcF9DbnRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IGMoIlJvdXRlU3RvcF9JRCIgPSAiUm91dGVTdG9wX0lEIikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzZWxlY3QoLVJvdXRlU3RvcF9DbnRQY3QpCgpkaW0oV2FpdFRpbWVfQXNOdW0pCmRpbShXYWl0VGltZV9SdGVDbnRzKQoKcm0oV2FpdFRpbWVfQXNOdW0pCnN0cihXYWl0VGltZV9SdGVDbnRzKQoKCiMgVG90YWwgcm93cwpucm93KFdhaXRUaW1lX1J0ZUNudHMpCgojIFJvd3Mgb2YgcmFyZSBSb3V0ZVN0b3BzCm5yb3coZmlsdGVyKFdhaXRUaW1lX1J0ZUNudHMsCiAgICAgICAgICAgIFJvdXRlU3RvcF9DbnROdW0gPD0gNjAKICAgICAgICAgICApCiAgICApIC8gbnJvdyhXYWl0VGltZV9SdGVDbnRzKQoKIyBSb3dzIG9mIGV4dHJlbWVseSBsb25nIHdhaXQgdGltZXMKbnJvdyhmaWx0ZXIoV2FpdFRpbWVfUnRlQ250cywKICAgICAgICAgICAgV2FpdFRpbWVfTWluMiA+IDE4MAogICAgICAgICAgICkKICAgICkgLyBucm93KFdhaXRUaW1lX1J0ZUNudHMpCgoKc2VsZWN0KFdhaXRUaW1lX1J0ZUNudHMsCiAgICAgICBXYWl0VGltZV9NaW4yCiAgICAgICkgJT4lIAogIHN1bW1hcnkoKQoKZmlsdGVyKFdhaXRUaW1lX1J0ZUNudHMsCiAgICAgICBSb3V0ZVN0b3BfQ250TnVtID4gNjAgICMgMTIgcGFzc2VzIHBlciBkYXkgaW4gYSA1LWRheSBkYXRhc2V0CiAgICAgICkgJT4lIAogIHNlbGVjdChXYWl0VGltZV9NaW4yKSAlPiUgCiAgc3VtbWFyeSgpCgpmaWx0ZXIoV2FpdFRpbWVfUnRlQ250cywKICAgICAgIFdhaXRUaW1lX01pbjIgPCAxODAgICMgcHJvYmFibHkgbWVhbnMgdGhhdCBzb21ldGhpbmcgd2VudCB3cm9uZwogICAgICApICU+JSAKICBzZWxlY3QoV2FpdFRpbWVfTWluMikgJT4lIAogIHN1bW1hcnkoKQoKYGBgCgoKQ29tcGFyZSBxdWFudGlsZXMgaW4gdGhlIGxpbWl0ZWQgZGF0YXNldHMuCmBgYHtyfQoKYSA8LSBhcy5kYXRhLmZyYW1lKHNlbGVjdChXYWl0VGltZV9SdGVDbnRzLAogICAgICAgICAgICAgICAgICAgICAgICAgIFdhaXRUaW1lX01pbjIKICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogICAgICAgICAgICAgICAgICAgICBxdWFudGlsZShwcm9icyA9IHNlcSgwLCAxLCAwLjAxKSwgbmEucm0gPSBUUlVFKQogICAgICAgICAgICAgICAgICApCgpiIDwtIGFzLmRhdGEuZnJhbWUoZmlsdGVyKFdhaXRUaW1lX1J0ZUNudHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgUm91dGVTdG9wX0NudE51bSA+IDYwCiAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KFdhaXRUaW1lX01pbjIpICU+JSAKICAgICAgICAgICAgICAgICAgICAgcXVhbnRpbGUocHJvYnMgPSBzZXEoMCwgMSwgMC4wMSksIG5hLnJtID0gVFJVRSkKICAgICAgICAgICAgICAgICAgKQoKYyA8LSBhcy5kYXRhLmZyYW1lKGZpbHRlcihXYWl0VGltZV9SdGVDbnRzLAogICAgICAgICAgICAgICAgICAgICAgICAgIFdhaXRUaW1lX01pbjIgPCAxODAKICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogICAgICAgICAgICAgICAgICAgICBzZWxlY3QoV2FpdFRpbWVfTWluMikgJT4lIAogICAgICAgICAgICAgICAgICAgICBxdWFudGlsZShwcm9icyA9IHNlcSgwLCAxLCAwLjAxKSwgbmEucm0gPSBUUlVFKQogICAgICAgICAgICAgICAgICApCgpXVF9GaWx0ZXJfUXVhbnRpbGVzIDwtIGJpbmRfY29scyhhLCBiLCBjKSAlPiUgCiAgbXV0YXRlKFF1YW50aWxlID0gc2VxKDAsIDEsIDAuMDEpCiAgICAgICAgKQoKY29sbmFtZXMoV1RfRmlsdGVyX1F1YW50aWxlcykgPC0gYygiQWxsIiwgIlJ0ZVN0cEFidjYwIiwgIldUQmx3MTgwIiwgIlF1YW50aWxlIikKcm0oYSwgYiwgYykKVmlldyhXVF9GaWx0ZXJfUXVhbnRpbGVzKQpXVF9GaWx0ZXJfUXVhbnRpbGVzCgpgYGAKCgpIaXN0b2dyYW0gb2YgYWxsIHdhaXQgdGltZXMuCmBgYHtyfQoKV2FpdFRpbWVfQWxsQnVzX0hpc3REZW4gPC0gZ2dwbG90KGZpbHRlcihzZWxlY3QoV2FpdFRpbWVfUnRlQ250cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV2FpdFRpbWVfTWluMgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIWlzLm5hKFdhaXRUaW1lX01pbjIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBXYWl0VGltZV9NaW4yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSAuLmRlbnNpdHkuLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNSwgZmlsbCA9ICJsaWdodGJsdWUiLCBjb2xvdXIgPSAiZ3JleTYwIiwgc2l6ZSA9IDAuMikgKwogIGdlb21fbGluZShzdGF0ID0gImRlbnNpdHkiLCBjb2xvdXIgPSAicmVkIikgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMzAwLCAzMCkKICAgICAgICAgICAgICAgICAgICApICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwgMzAwKSwKICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwgMC4wMzUpCiAgICAgICAgICAgICAgICAgKSArCiAgbGFicyh0aXRsZSA9ICJWYXJpYXRpb24gaW4gV2FpdCBUaW1lIiwKICAgICAgIHggPSAiV2FpdCBUaW1lIChtaW4pIiwKICAgICAgIHkgPSAiRGVuc2l0eSIKICAgICAgKQoKV2FpdFRpbWVfQWxsQnVzX0hpc3REZW4KCmBgYAoKCkJveCBwbG90cyBmb3IgV2FpdFRpbWUgKGFsbCBidXNzZXMsIGJ5IFppcCBDb2RlKS4KYGBge3J9CgojIENvdW50X1ZhbHVlcyBpcyBuZWVkZWQgdG8gZGlzcGxheSB0aGUgbWVkaWFucyBvbiB0aGUgYm94IHBsb3RzCkJ1c1JvdXRlIDwtIHNlbGVjdChXYWl0VGltZV9SdGVDbnRzLAogICAgICAgICAgICAgICAgICAgUm91dGUsCiAgICAgICAgICAgICAgICAgICBXYWl0VGltZV9NaW4yLAogICAgICAgICAgICAgICAgICAgU3RvcF9aaXAKICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgZmlsdGVyKFJvdXRlID09ICJYMiIpCgpDb3VudFZhbHVlc19BbGxCdXNfWmlwIDwtIGRkcGx5KEJ1c1JvdXRlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC4oU3RvcF9aaXApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBWYWx1ZV9Db3VudHMgPSBtZWRpYW4oV2FpdFRpbWVfTWluMiwgbmEucm0gPSBUUlVFKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKV2FpdFRpbWVfQWxsQnVzX1ppcF9Cb3ggPC0gZ2dwbG90KEJ1c1JvdXRlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGZhY3RvcihTdG9wX1ppcCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV2FpdFRpbWVfTWluMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gZmFjdG9yKFN0b3BfWmlwKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsgCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuY29sb3VyPSJyZWQiLCBub3RjaD1UUlVFLCBuYS5ybSA9IFRSVUUpICsKICBnZW9tX3RleHQoZGF0YSA9IENvdW50VmFsdWVzX0FsbEJ1c19aaXAsCiAgICAgICAgICAgIGFlcyh5ID0gVmFsdWVfQ291bnRzLAogICAgICAgICAgICAgICAgbGFiZWwgPSBmb3JtYXQocm91bmQoVmFsdWVfQ291bnRzLCBkaWdpdHMgPSAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zbWFsbCA9IDEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICApLAogICAgICAgICAgICBzaXplID0gMywKICAgICAgICAgICAgdmp1c3QgPSAtMC41CiAgICAgICAgICAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUpKSArCiAgY29vcmRfY2FydGVzaWFuKCMgeGxpbSA9IGMoMCwgMTgwKSwKICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwgNDUpCiAgICAgICAgICAgICAgICAgKSArCiAgbGFicyh0aXRsZSA9ICJXYWl0aW5nIFRpbWUgYXQgYSBHaXZlbiBTdG9wIChmb3IgdGhlIFgyKSIsCiAgICAgICB4ID0gIlppcCBDb2RlIG9mIERlc3RpbmF0aW9uIiwKICAgICAgIHkgPSAiV2FpdGluZyBUaW1lIChtaW4pIgogICAgICApCgpXYWl0VGltZV9BbGxCdXNfWmlwX0JveAoKYGBgCgoKVGVzdCBpbnZlc3RpZ2F0aW9uIG9mIGp1c3QgdGhlIFgyIFJvdXRlLiBWaW9saW4gcGxvdHMgZm9yIHRpbWUgYmV0d2VlbiBidXMgYXJyaXZhbHMgKGJ5IFppcCBDb2RlKS4KYGBge3J9CgpXYWl0VGltZV9BbGxCdXNfWmlwX1Zpb2xpbiA8LSBnZ3Bsb3QoQnVzUm91dGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoZmFjdG9yKFN0b3BfWmlwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBXYWl0VGltZV9NaW4yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBmYWN0b3IoU3RvcF9aaXApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgKyAKICBnZW9tX3Zpb2xpbihkcmF3X3F1YW50aWxlcyA9IGMoMC4yNSwgMC41LCAwLjc1KSwKICAgICAgICAgICAgICB0cmltID0gVFJVRSwKICAgICAgICAgICAgICBzY2FsZSA9ICJjb3VudCIsCiAgICAgICAgICAgICAgbmEucm0gPSBUUlVFLAogICAgICAgICAgICAgIHNob3cubGVnZW5kID0gTkEsCiAgICAgICAgICAgICAgaW5oZXJpdC5hZXMgPSBUUlVFCiAgICAgICAgICAgICApICsKICBnZW9tX3RleHQoZGF0YSA9IENvdW50VmFsdWVzX0FsbEJ1c19aaXAsCiAgICAgICAgICAgIGFlcyh5ID0gVmFsdWVfQ291bnRzLAogICAgICAgICAgICAgICAgbGFiZWwgPSBmb3JtYXQocm91bmQoVmFsdWVfQ291bnRzLCBkaWdpdHMgPSAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zbWFsbCA9IDEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICApLAogICAgICAgICAgICBzaXplID0gMy41LAogICAgICAgICAgICB2anVzdCA9IC0wLjUKICAgICAgICAgICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSkpICsKICBjb29yZF9jYXJ0ZXNpYW4oIyB4bGltID0gYygwLCAxODApLAogICAgICAgICAgICAgICAgICB5bGltID0gYygwLCA0NSkKICAgICAgICAgICAgICAgICApICsKICBsYWJzKHRpdGxlID0gIldhaXRpbmcgVGltZSBhdCBhIEdpdmVuIFN0b3AgKGZvciB0aGUgWDIpIiwKICAgICAgIHggPSAiWmlwIENvZGUgb2YgRGVzdGluYXRpb24iLAogICAgICAgeSA9ICJXYWl0aW5nIFRpbWUgKG1pbikiCiAgICAgICkKClRpbWVCdHdFdmVudHNfWDJfVmlvbGluUGxvdF96CgpgYGAKCgpCb3ggcGxvdHMgZm9yIFdhaXRUaW1lIChaaXAgQ29kZSwgYnkgSG91ckdyb3VwWmlwKS4KYGBge3J9CgojIENvdW50X1ZhbHVlcyBpcyBuZWVkZWQgdG8gZGlzcGxheSB0aGUgbWVkaWFucyBvbiB0aGUgYm94IHBsb3RzClppcCA8LSBzZWxlY3QoV2FpdFRpbWVfUnRlQ250cywKICAgICAgICAgICAgICBSb3V0ZSwKICAgICAgICAgICAgICBXYWl0VGltZV9NaW4yLAogICAgICAgICAgICAgIFN0b3BfWmlwLAogICAgICAgICAgICAgIEV2ZW50X1RpbWVfSHJHcm91cAogICAgICAgICAgICAgKSAlPiUgCiAgZmlsdGVyKFN0b3BfWmlwID09IDIwMDAyKQoKQ291bnRWYWx1ZXNfQWxsQnVzX0hHIDwtIGRkcGx5KFppcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC4oRXZlbnRfVGltZV9Ickdyb3VwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFZhbHVlX0NvdW50cyA9IG1lZGlhbihXYWl0VGltZV9NaW4yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKV2FpdFRpbWVfQWxsQnVzX0hHX0JveCA8LSBnZ3Bsb3QoWmlwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoZmFjdG9yKEV2ZW50X1RpbWVfSHJHcm91cCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBXYWl0VGltZV9NaW4yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsgCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuY29sb3VyPSJyZWQiLCBub3RjaD1UUlVFLCBuYS5ybSA9IFRSVUUpICsKICBnZW9tX3RleHQoZGF0YSA9IENvdW50VmFsdWVzX0FsbEJ1c19IRywKICAgICAgICAgICAgYWVzKHkgPSBWYWx1ZV9Db3VudHMsCiAgICAgICAgICAgICAgICBsYWJlbCA9IGZvcm1hdChyb3VuZChWYWx1ZV9Db3VudHMsIGRpZ2l0cyA9IDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnNtYWxsID0gMQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICksCiAgICAgICAgICAgIHNpemUgPSAyLjUsCiAgICAgICAgICAgIHZqdXN0ID0gLTAuNQogICAgICAgICAgICkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTQ1KSkgKwogIGNvb3JkX2NhcnRlc2lhbigjIHhsaW0gPSBjKDAsIDE4MCksCiAgICAgICAgICAgICAgICAgIHlsaW0gPSBjKDAsIDQ1KQogICAgICAgICAgICAgICAgICkgKwogIGxhYnModGl0bGUgPSAiV2FpdGluZyBUaW1lIGF0IGEgR2l2ZW4gU3RvcCAoZm9yIFppcCAyMDAwMikiLAogICAgICAgeCA9ICJIb3VyIEdyb3VwIiwKICAgICAgIHkgPSAiV2FpdGluZyBUaW1lIChtaW4pIgogICAgICApCiAgIyBmYWNldF93cmFwKH5TdG9wX1ppcAogICMgICAgICAgICAgICAjIG5yb3cgPSA1CiAgIyAgICAgICAgICAgKQoKV2FpdFRpbWVfQWxsQnVzX0hHX0JveAoKYGBgCgoKVmlvbGluIHBsb3RzIGZvciBXYWl0VGltZSAoWmlwIENvZGUsIGJ5IEhvdXJHcm91cFppcCkuCmBgYHtyfQoKV2FpdFRpbWVfQWxsQnVzX0hHX1ZsbiA8LSBnZ3Bsb3QoWmlwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoZmFjdG9yKEV2ZW50X1RpbWVfSHJHcm91cCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBXYWl0VGltZV9NaW4yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsgCiAgZ2VvbV92aW9saW4oZHJhd19xdWFudGlsZXMgPSBjKDAuMjUsIDAuNSwgMC43NSksCiAgICAgICAgICAgICAgdHJpbSA9IFRSVUUsCiAgICAgICAgICAgICAgc2NhbGUgPSAiY291bnQiLAogICAgICAgICAgICAgIG5hLnJtID0gVFJVRSwKICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IE5BLAogICAgICAgICAgICAgIGluaGVyaXQuYWVzID0gVFJVRQogICAgICAgICAgICAgKSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBDb3VudFZhbHVlc19BbGxCdXNfSEcsCiAgICAgICAgICAgIGFlcyh5ID0gVmFsdWVfQ291bnRzLAogICAgICAgICAgICAgICAgbGFiZWwgPSBmb3JtYXQocm91bmQoVmFsdWVfQ291bnRzLCBkaWdpdHMgPSAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zbWFsbCA9IDEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICApLAogICAgICAgICAgICBzaXplID0gMi41LAogICAgICAgICAgICB2anVzdCA9IC0wLjUKICAgICAgICAgICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSkpICsKICBjb29yZF9jYXJ0ZXNpYW4oIyB4bGltID0gYygwLCAxODApLAogICAgICAgICAgICAgICAgICB5bGltID0gYygwLCA5MCkKICAgICAgICAgICAgICAgICApICsKICBsYWJzKHRpdGxlID0gIldhaXRpbmcgVGltZSBhdCBhIEdpdmVuIFN0b3AgKGZvciBaaXAgMjAwMDIpIiwKICAgICAgIHggPSAiSG91ciBHcm91cCIsCiAgICAgICB5ID0gIldhaXRpbmcgVGltZSAobWluKSIKICAgICAgKQogICMgZmFjZXRfd3JhcCh+U3RvcF9aaXAKICAjICAgICAgICAgICAgIyBucm93ID0gNQogICMgICAgICAgICAgICkKCldhaXRUaW1lX0FsbEJ1c19IR19WbG4KCmBgYAoKCkJveCBwbG90cyBmb3IgV2FpdFRpbWUgKFJvdXRlLCBieSBIb3VyR3JvdXBaaXApLgpgYGB7cn0KCiMgQ291bnRfVmFsdWVzIGlzIG5lZWRlZCB0byBkaXNwbGF5IHRoZSBtZWRpYW5zIG9uIHRoZSBib3ggcGxvdHMKUnRlIDwtIHNlbGVjdChXYWl0VGltZV9SdGVDbnRzLAogICAgICAgICAgICAgIFJvdXRlLAogICAgICAgICAgICAgIFdhaXRUaW1lX01pbjIsCiAgICAgICAgICAgICAgU3RvcF9aaXAsCiAgICAgICAgICAgICAgRXZlbnRfVGltZV9Ickdyb3VwCiAgICAgICAgICAgICApICU+JSAKICBmaWx0ZXIoUm91dGUgPT0gIlgyIikKCkNvdW50VmFsdWVzX0FsbEJ1c19SdGVIRyA8LSBncm91cF9ieShSdGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0hyR3JvdXAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKAogICAgVmFsdWVfQ291bnRzID0gbWVkaWFuKFdhaXRUaW1lX01pbjIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgVkMgPSBxdWFudGlsZShXYWl0VGltZV9NaW4yLCBwcm9icyA9IDAuOSwgbmEucm0gPSBUUlVFKQogICAgKQoKCldhaXRUaW1lX0FsbEJ1c19SdGVIR19Cb3ggPC0gZ2dwbG90KFJ0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV2FpdFRpbWVfTWluMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBmYWN0b3IoRXZlbnRfVGltZV9Ickdyb3VwKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArIAogIGdlb21fYm94cGxvdChvdXRsaWVyLmNvbG91cj0icmVkIiwgbm90Y2g9VFJVRSwgbmEucm0gPSBUUlVFKSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBDb3VudFZhbHVlc19BbGxCdXNfUnRlSEcsCiAgICAgICAgICAgIGFlcyh5ID0gVmFsdWVfQ291bnRzLAogICAgICAgICAgICAgICAgbGFiZWwgPSBmb3JtYXQocm91bmQoVmFsdWVfQ291bnRzLCBkaWdpdHMgPSAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zbWFsbCA9IDEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICApLAogICAgICAgICAgICBzaXplID0gMi41LAogICAgICAgICAgICB2anVzdCA9IC0wLjUKICAgICAgICAgICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSkpICsKICBjb29yZF9jYXJ0ZXNpYW4oIyB4bGltID0gYygwLCAxODApLAogICAgICAgICAgICAgICAgICB5bGltID0gYygwLCBtYXgoQ291bnRWYWx1ZXNfQWxsQnVzX1J0ZUhHJFZDKSkKICAgICAgICAgICAgICAgICApICsKICBsYWJzKHRpdGxlID0gIldhaXRpbmcgVGltZSBhdCBhIEdpdmVuIFN0b3AiLAogICAgICAgc3VidGl0bGUgPSAoIlJvdXRlIFgyIiksCiAgICAgICB4ID0gIkhvdXIgR3JvdXAiLAogICAgICAgeSA9ICJXYWl0aW5nIFRpbWUgKG1pbikiCiAgICAgICkgCiMgKwojICAgZmFjZXRfd3JhcCh+U3RvcF9aaXAKIyAgICAgICAgICAgICAgIyBucm93ID0gNQojICAgICAgICAgICAgICkKCldhaXRUaW1lX0FsbEJ1c19SdGVIR19Cb3gKCmBgYAoKClZpb2xpbiBwbG90cyBmb3IgV2FpdFRpbWUgKFppcCBDb2RlLCBieSBIb3VyR3JvdXBaaXApLgpgYGB7cn0KCldhaXRUaW1lX0FsbEJ1c19SdGVIR19WbG4gPC0gZ2dwbG90KFJ0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV2FpdFRpbWVfTWluMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBmYWN0b3IoRXZlbnRfVGltZV9Ickdyb3VwKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArIAogIGdlb21fdmlvbGluKGRyYXdfcXVhbnRpbGVzID0gYygwLjI1LCAwLjUsIDAuNzUpLAogICAgICAgICAgICAgIHRyaW0gPSBUUlVFLAogICAgICAgICAgICAgIHNjYWxlID0gImNvdW50IiwKICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUsCiAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBOQSwKICAgICAgICAgICAgICBpbmhlcml0LmFlcyA9IFRSVUUKICAgICAgICAgICAgICkgKwogIGdlb21fdGV4dChkYXRhID0gQ291bnRWYWx1ZXNfQWxsQnVzX1J0ZUhHLAogICAgICAgICAgICBhZXMoeSA9IFZhbHVlX0NvdW50cywKICAgICAgICAgICAgICAgIGxhYmVsID0gZm9ybWF0KHJvdW5kKFZhbHVlX0NvdW50cywgZGlnaXRzID0gMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuc21hbGwgPSAxCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgc2l6ZSA9IDIuNSwKICAgICAgICAgICAgdmp1c3QgPSAtMC41CiAgICAgICAgICAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUpKSArCiAgY29vcmRfY2FydGVzaWFuKCMgeGxpbSA9IGMoMCwgMTgwKSwKICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwgNDUpCiAgICAgICAgICAgICAgICAgKSArCiAgbGFicyh0aXRsZSA9ICJXYWl0aW5nIFRpbWUgYXQgYSBHaXZlbiBTdG9wIiwKICAgICAgIHN1YnRpdGxlID0gKCIoUm91dGUgWDIpIiksCiAgICAgICB4ID0gIkhvdXIgR3JvdXAiLAogICAgICAgeSA9ICJXYWl0aW5nIFRpbWUgKG1pbikiCiAgICAgICkgKwogIGZhY2V0X3dyYXAoflN0b3BfWmlwCiAgICAgICAgICAgICAjIG5yb3cgPSA1CiAgICAgICAgICAgICkKCldhaXRUaW1lX0FsbEJ1c19SdGVIR19WbG4KCmBgYAoKClgyIFBlcmNlbnRpbGVzIExpbmUgR3JhcGggVGVzdC4KYGBge3J9CgpYMl9QY3QgPC0gc2VsZWN0KFdhaXRUaW1lX1J0ZUNudHMsCiAgICAgICAgICAgICAgICAgUm91dGUsCiAgICAgICAgICAgICAgICAgU3RvcF9aaXAsCiAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9EYXRlLAogICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfRGF5LAogICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfSHJHcm91cCwKICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0hyLAogICAgICAgICAgICAgICAgIExhdGl0dWRlLAogICAgICAgICAgICAgICAgIExvbmdpdHVkZSwKICAgICAgICAgICAgICAgICBXYWl0VGltZV9NaW4yCiAgICAgICAgICAgICAgICApICU+JSAKICBmaWx0ZXIoUm91dGUgPT0gIlgyIikgJT4lIAogIGdyb3VwX2J5KEV2ZW50X1RpbWVfSHIsCiAgICAgICAgICAgU3RvcF9aaXAKICAgICAgICAgICkgJT4lIAogIHN1bW1hcmlzZShQY3Q1MCA9IHF1YW50aWxlKFdhaXRUaW1lX01pbjIsIHByb2JzID0gMC41LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBQY3Q2MCA9IHF1YW50aWxlKFdhaXRUaW1lX01pbjIsIHByb2JzID0gMC42LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBQY3Q3MCA9IHF1YW50aWxlKFdhaXRUaW1lX01pbjIsIHByb2JzID0gMC43LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBQY3Q4MCA9IHF1YW50aWxlKFdhaXRUaW1lX01pbjIsIHByb2JzID0gMC44LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBQY3Q5MCA9IHF1YW50aWxlKFdhaXRUaW1lX01pbjIsIHByb2JzID0gMC45LCBuYS5ybSA9IFRSVUUpCiAgICAgICAgICAgKQoKc3RyKFgyX1BjdCkKVmlldyhYMl9QY3QpCgoKWDJfTG9uZyA8LSBnYXRoZXIoWDJfUGN0LAogICAgICAgICAgICAgICAgICBrZXkgPSBQZXJjZW50aWxlLAogICAgICAgICAgICAgICAgICB2YWx1ZSA9IFBjdGlsZSwKICAgICAgICAgICAgICAgICAgUGN0NTAsCiAgICAgICAgICAgICAgICAgIFBjdDYwLAogICAgICAgICAgICAgICAgICBQY3Q3MCwKICAgICAgICAgICAgICAgICAgUGN0ODAsCiAgICAgICAgICAgICAgICAgIFBjdDkwCiAgICAgICAgICAgICAgICApCgpzdHIoWDJfTG9uZykKVmlldyhYMl9Mb25nKQoKClgyX1dhaXRCeUhyX0xpbmUgPC0gZ2dwbG90KFgyX0xvbmcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gRXZlbnRfVGltZV9IciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBQY3RpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWN0b3IoUGVyY2VudGlsZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IFBlcmNlbnRpbGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICkgKwogIGdlb21fbGluZSgpICsKICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iCiAgICAgICApICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwgMjMpCiAgICAgICAgICAgICAgICAgICMgeWxpbSA9IGMoMCwgNDUpCiAgICAgICAgICAgICAgICAgKSArIAogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMjMsIDIpCiAgICAgICAgICAgICAgICAgICAgKSArCiAgbGFicyh0aXRsZSA9ICJXYWl0aW5nIFRpbWUgVGhyb3VnaG91dCB0aGUgRGF5IiwKICAgICAgIHN1YnRpdGxlID0gKCIoUm91dGUgWDIpIiksCiAgICAgICB4ID0gIkhvdXIgb2YgdGhlIERheSIsCiAgICAgICB5ID0gIldhaXRpbmcgVGltZSAobWluKSIKICAgICAgKSArCiAgZmFjZXRfd3JhcCh+U3RvcF9aaXApCgpYMl9XYWl0QnlIcl9MaW5lCgpgYGAKCgoKR0VUIERBVEEgUkVBRFkgRk9SIFNISU5ZICAtLSAgR0VUIERBVEEgUkVBRFkgRk9SIFNISU5ZICAtLSAgR0VUIERBVEEgUkVBRFkgRk9SIFNISU5ZCkdFVCBEQVRBIFJFQURZIEZPUiBTSElOWSAgLS0gIEdFVCBEQVRBIFJFQURZIEZPUiBTSElOWSAgLS0gIEdFVCBEQVRBIFJFQURZIEZPUiBTSElOWQpHRVQgREFUQSBSRUFEWSBGT1IgU0hJTlkgIC0tICBHRVQgREFUQSBSRUFEWSBGT1IgU0hJTlkgIC0tICBHRVQgREFUQSBSRUFEWSBGT1IgU0hJTlkKCkJhc2VEYXRhOiBVc2VkIGluIHBsb3RzIGJ5IGhvdXIgYW5kIHppcGNvZGUgKGZpcnN0IHR3byBTaGlueSB0YWJzKS4KYGBge3J9CgojIHN0cihXYWl0VGltZV9SdGVDbnRzKQoKU2hpbnlfV2FpdERhdGFfQmFzZSA8LSBzZWxlY3QoV2FpdFRpbWVfUnRlQ250cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUm91dGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0b3BfWmlwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0RhdGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfRGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0hyR3JvdXAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfSHIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExhdGl0dWRlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBMb25naXR1ZGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFdhaXRUaW1lX01pbjIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBtdXRhdGUoRXZlbnRfVGltZV9Zck10aERheUhyID0gZmxvb3JfZGF0ZShFdmVudF9UaW1lLCAiaG91ciIpCiAgICAgICAgKSAlPiUgCiAgcmVuYW1lKFppcENvZGUgPSBTdG9wX1ppcCwKICAgICAgICAgSG91ckdyb3VwID0gRXZlbnRfVGltZV9Ickdyb3VwLAogICAgICAgICBEYXRlID0gRXZlbnRfVGltZV9EYXRlLAogICAgICAgICBEYXkgPSBFdmVudF9UaW1lX0RheSwKICAgICAgICAgSG91ciA9IEV2ZW50X1RpbWVfSHIsCiAgICAgICAgIFdhaXRUaW1lX01pbiA9IFdhaXRUaW1lX01pbjIKICAgICAgICApICU+JSAKICBmaWx0ZXIoV2FpdFRpbWVfTWluIDw9IDE4MCkKClNoaW55X1dhaXREYXRhX0Jhc2UkUm91dGUgPC0gZmFjdG9yKFNoaW55X1dhaXREYXRhX0Jhc2UkUm91dGUpCgpzdHIoU2hpbnlfV2FpdERhdGFfQmFzZSkKVmlldyh0YWlsKFNoaW55X1dhaXREYXRhX0Jhc2UsIDUwMCkpCgpzYXZlUkRTKFNoaW55X1dhaXREYXRhX0Jhc2UsCiAgICAgICAgIlNoaW55X1dhaXREYXRhX0Jhc2UucmRzIgogICAgICAgKQoKYGBgCgoKUHJlcCBkYXRhIGZvciBtYXBwaW5nLgpgYGB7cn0KCiMgZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJka2FobGUvZ2dtYXAiKQojIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiaGFkbGV5L2dncGxvdDIiKQojIGluc3RhbGwucGFja2FnZXMoImdnbWFwIiwgdHlwZSA9ICJzb3VyY2UiKQoKIyBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoJ2hhZGxleS9nZ3Bsb3QyJykKZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJoYWRsZXkvZ2dwbG90MkB2Mi4yLjAiKQojIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigndGhvbWFzcDg1L2dnZm9yY2UnKQojIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigndGhvbWFzcDg1L2dncmFwaCcpCiMgZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCdzbG93a293L2dncmVwZWwnKQoKCnRyYWN0IDwtIAogIHJlYWRPR1IoZHNuID0gIi9Vc2Vycy9tZHR1cnNlL0Rlc2t0b3AvQW5hbHl0aWNzL0RDTWV0cm9CdXMvdGxfMjAxNl91c196Y3RhNTEwIiwKICAgICAgICAgIGxheWVyID0gInRsXzIwMTZfdXNfemN0YTUxMCIKICAgICAgICAgKQogIApjbGFzcyh0cmFjdCkKCiMgY29udmVydCB0aGUgR0VPSUQgdG8gYSBjaGFyYWN0ZXIKdHJhY3RAZGF0YSRHRU9JRCA8LSBhcy5jaGFyYWN0ZXIodHJhY3RAZGF0YSRHRU9JRCkKc3RyKHRyYWN0QGRhdGEpCgoKZ2d0cmFjdCA8LSB0aWR5KHRyYWN0LCByZWdpb24gPSAiR0VPSUQiKQoKIyBzdHIoZ2d0cmFjdCkKIyBzdW1tYXJ5KGdndHJhY3QpCiMgVmlldyhoZWFkKGdndHJhY3QsIDUwKSkKCgoKIyBzdHIoU2hpbnlfV2FpdERhdGFfQmFzZSkKClppcFdhaXRUZXN0IDwtIGZpbHRlcihTaGlueV9XYWl0RGF0YV9CYXNlLAogICAgICAgICAgICAgICAgICAgICAgV2FpdFRpbWVfTWluIDw9IDE4MCAmCiAgICAgICAgICAgICAgICAgICAgICAgICFpcy5uYShaaXBDb2RlKQogICAgICAgICAgICAgICAgICAgICApICU+JSAKICBncm91cF9ieShaaXBDb2RlLAogICAgICAgICAgIEV2ZW50X1RpbWVfWXJNdGhEYXlIcgogICAgICAgICAgICMgRXZlbnRfVGltZV9EYXksCiAgICAgICAgICAgIyBFdmVudF9UaW1lX0hyCiAgICAgICAgICApICU+JSAKICBzdW1tYXJpc2UoUGN0ODAgPSBxdWFudGlsZShXYWl0VGltZV9NaW4sIHByb2JzID0gMC44LCBuYS5ybSA9IFRSVUUpCiAgICAgICAgICAgKSAlPiUgCiAgYXJyYW5nZSgjIEV2ZW50X1RpbWVfSHIsCiAgICAgICAgICBaaXBDb2RlLAogICAgICAgICAgRXZlbnRfVGltZV9Zck10aERheUhyCiAgICAgICAgICkgJT4lIAogIGFzLmRhdGEuZnJhbWUoKSAlPiUgCiAgbXV0YXRlKEV2ZW50X1RpbWVfRGF0ZU5ldyA9IGZsb29yX2RhdGUoRXZlbnRfVGltZV9Zck10aERheUhyLCAiZGF5IiksCiAgICAgICAgIEV2ZW50X1RpbWVfSHJOZXcgPSBob3VyKEV2ZW50X1RpbWVfWXJNdGhEYXlIciksCiAgICAgICAgIFBjdDgwX0xldmVsID0gZmFjdG9yKGlmZWxzZShQY3Q4MCA8IDEwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJlbG93IDEwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFBjdDgwIDwgMjAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQmVsb3cgMjAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoUGN0ODAgPCAzMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJCZWxvdyAzMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShQY3Q4MCA8IDQwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJlbG93IDQwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFBjdDgwIDwgNTAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQmVsb3cgNTAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoUGN0ODAgPCA2MCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJCZWxvdyA2MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiNjAgUGx1cyIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSkpKSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJCZWxvdyAxMCIsICJCZWxvdyAyMCIsICJCZWxvdyAzMCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJCZWxvdyA0MCIsICJCZWxvdyA1MCIsICJCZWxvdyA2MCIsICI2MCBQbHVzIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZCA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKQoKc3RyKFppcFdhaXRUZXN0KQpaaXBXYWl0VGVzdCRaaXBDb2RlIDwtIGFzLmNoYXJhY3RlcihaaXBXYWl0VGVzdCRaaXBDb2RlKQpzdHIoWmlwV2FpdFRlc3QpCnN1bW1hcnkoWmlwV2FpdFRlc3QpCgpWaWV3KGhlYWQoWmlwV2FpdFRlc3QsIDUwMCkpCgoKU3RvcFppcF9MZWZ0IDwtIGxlZnRfam9pbihaaXBXYWl0VGVzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICBnZ3RyYWN0LAogICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygiWmlwQ29kZSIgPSAiaWQiKQogICAgICAgICAgICAgICAgICAgICAgICAgKQoKc3RyKFN0b3BaaXBfTGVmdCkKc3VtbWFyeShTdG9wWmlwX0xlZnQpCgpgYGAKCgpUZXN0IG1hcHBpbmcgZnVuY3Rpb25hbHRpeS4KYGBge3J9CgptYXAgPC0gZ2V0X21hcChsb2NhdGlvbiA9IGMobG9uID0gLTc3LjAzNjc2LCBsYXQgPSAzOC44OTc4NCksCiAgICAgICAgICAgICAgIHNvdXJjZSA9ICJnb29nbGUiLAogICAgICAgICAgICAgICAjIG1hcHR5cGUgPSAicm9hZG1hcCIKICAgICAgICAgICAgICAgem9vbSA9IDEyCiAgICAgICAgICAgICAgKQoKZ2dtYXAobWFwKSArCiAgZ2VvbV9wb2x5Z29uKGFlcyh4ID0gbG9uZywgCiAgICAgICAgICAgICAgICAgICB5ID0gbGF0LCAKICAgICAgICAgICAgICAgICAgIGdyb3VwID0gZ3JvdXAsCiAgICAgICAgICAgICAgICAgICBmaWxsID0gUGN0ODBfTGV2ZWwKICAgICAgICAgICAgICAgICAgKSwgCiAgICAgICAgICAgICAgIGRhdGEgPSBmaWx0ZXIoU3RvcFppcF9MZWZ0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfWXJNdGhEYXlIciA9PSBhcy5QT1NJWGN0KCIyMDE2LTEwLTA3IDIwOjAwOjAwIikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjICAgU3RvcF9aaXAgPT0gIjIwMDAzIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgY29sb3VyID0gImdyYXkxIiwgCiAgICAgICAgICAgICAgICMgZmlsbCA9ICdibGFjaycsIAogICAgICAgICAgICAgICBhbHBoYSA9IC40LCAKICAgICAgICAgICAgICAgc2l6ZSA9IC4zCiAgICAgICAgICAgICAgKSArCiMgKwogICMgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3VycyA9IGMoIndoaXRlIiwgInJveWFsYmx1ZTQiLCAicmVkIiksCiAgIyAgICAgICAgICAgICAgICAgICAgICAjICAibGlnaHRzdGVlbGJsdWU0IiwKICAjICAgICAgICAgICAgICAgICAgICAgICMgImxpZ2h0cGluazEiLAogICMgICAgICAgICAgICAgICAgICAgICAgIyB2YWx1ZXM9Y2JQYWxldHRlLAogICMgICAgICAgICAgICAgICAgICAgICAgIyB2YWx1ZXMgPSBjKDEsMC41LCAuMywgLjIsIC4xLCAwKQogICMgICAgICAgICAgICAgICAgICAgICAgbmEudmFsdWUgPSAiYmxhY2siLAogICMgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYyhzZXEoMCwgMTgwLCAzMCkpCiAgIyAgICAgICAgICAgICAgICAgICAgICAjIHZhbHVlcyA9IHJlc2NhbGUoKQogICMgICAgICAgICAgICAgICAgICAgICApIAojICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNwZWN0cmFsIiwgIyAiWWxPclJkIiAjICJTZXQxIiwKICAgICAgICAgICAgICAgICAgICBkaXJlY3Rpb24gPSAtMSwKICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBsZXZlbHMoU3RvcFppcF9MZWZ0JFBjdDgwX0xldmVsKQogICAgICAgICAgICAgICAgICAgKQoKYGBgCgoKU2hpbnkgZGF0YSBmb3IgbWFwcGluZyAodXNlZCBpbiAzcmQgdGFiKS4KYGBge3J9CgpWaWV3KGhlYWQoZmlsdGVyKFN0b3BaaXBfTGVmdCwKICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0hyTmV3ID09IDE1CiAgICAgICAgICAgICAgICApLAogICAgICAgICAgNTAwCiAgICAgICAgICkKICAgICkKClNoaW55X1dhaXREYXRhX01hcCA8LSBTdG9wWmlwX0xlZnQgJT4lIAogIHJlbmFtZShZck10aERheUhyID0gRXZlbnRfVGltZV9Zck10aERheUhyLAogICAgICAgICBZck10aERheSA9IEV2ZW50X1RpbWVfRGF0ZU5ldywKICAgICAgICAgSG91ciA9IEV2ZW50X1RpbWVfSHJOZXcKICAgICAgICApCgpzdHIoU2hpbnlfV2FpdERhdGFfTWFwKQoKClNoaW55X1dhaXREYXRhX01hcF9XZWQgPC0gZmlsdGVyKFNoaW55X1dhaXREYXRhX01hcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgWXJNdGhEYXkgPT0gYXMuUE9TSVhjdCgiMjAxNi0xMC0wNSIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKc3RyKFNoaW55X1dhaXREYXRhX01hcF9XZWQpCnN1bW1hcnkoU2hpbnlfV2FpdERhdGFfTWFwX1dlZCkKCgpzYXZlUkRTKFNoaW55X1dhaXREYXRhX01hcCwKICAgICAgICAiU2hpbnlfV2FpdERhdGFfTWFwLnJkcyIKICAgICAgICkKCnNhdmVSRFMoU2hpbnlfV2FpdERhdGFfTWFwX1dlZCwKICAgICAgICAiU2hpbnlfV2FpdERhdGFfTWFwX1dlZC5yZHMiCiAgICAgICApCgpgYGAKCgoKCkNsdXN0ZXJpbmcKCkRhdGEgcHJlcC4KYGBge3J9CgpybSh0cmFjdCwgZ2d0cmFjdCwgU3RvcFppcF9MZWZ0LCBaaXBXYWl0VGVzdCwgU2hpbnlfV2FpdERhdGFfQmFzZSwgU2hpbnlfV2FpdERhdGFfTWFwLCBTaGlueV9XYWl0RGF0YV9NYXBfV2VkKQoKCmRpbShOZXdUcmF2VGltZSkKZGltKFdhaXRUaW1lX1J0ZUNudHMpCgoKc3RyKHNlbGVjdChOZXdUcmF2VGltZSwKICAgICAgICAgICAtbWF0Y2hlcygiKHEoMnw1fCg5NSl8KDk4KSkpfE1lYW58TWVkfENudCIpCiAgICAgICAgICApCiAgICkKc3RyKHNlbGVjdChOZXdUcmF2VGltZSwKICAgICAgICAgICBtYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICkKICAgKQpzdHIoV2FpdFRpbWVfUnRlQ250cykKCiAKIAojIENsdXN0RGF0YSA8LSBzZWxlY3QoV2FpdFRpbWVfUnRlQ250cywKIyAgICAgICAgICAgICAgICAgICAgIGdyb3VwLAojICAgICAgICAgICAgICAgICAgICAgQnVzRGF5X0V2ZW50TnVtLAojICAgICAgICAgICAgICAgICAgICAgUm91dGUsCiMgICAgICAgICAgICAgICAgICAgICBSdGVDaGFuZ2UyLAojICAgICAgICAgICAgICAgICAgICAgUm91dGVBbHQsCiMgICAgICAgICAgICAgICAgICAgICBEaXJDaGFuZ2UyLAojICAgICAgICAgICAgICAgICAgICAgUm91dGVfRGlyZWN0aW9uLAojICAgICAgICAgICAgICAgICAgICAgU3RvcF9TZXF1ZW5jZSwKIyAgICAgICAgICAgICAgICAgICAgIFN0b3BJRF9JbmRpY2F0b3IsCiMgICAgICAgICAgICAgICAgICAgICBTdG9wX0NvdW50eSwKIyAgICAgICAgICAgICAgICAgICAgIFN0b3BfQ2l0eSwKIyAgICAgICAgICAgICAgICAgICAgIFN0b3BfWmlwLAojICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9IciwKIyAgICAgICAgICAgICAgICAgICAgIER3ZWxsX1RpbWUyLAojICAgICAgICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycywKIyAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnNfTGFiZWwsCiMgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfTmV3LAojICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX05ld19MYWJlbCwKIyAgICAgICAgICAgICAgICAgICAgIFdhaXRUaW1lX01pbjIKIyAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiMgICBmaWx0ZXIoV2FpdFRpbWVfTWluMiA8PSAxODApICU+JSAKIyAgIG11dGF0ZShTcGVlZEF2Z19NcGhfVERNTkhfVFRTTiA9IFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMgLyAKIyAgICAgICAgICAgIChUVF9TZWNfTmV3IC8gNjAgLyA2MCkKIyAgICAgICAgICkKIyAlPiUgCiMgICBzZWxlY3RfaWYoQ2x1c3REYXRhLAojICAgICAgICAgICAgIGZ1bmN0aW9uKGNvbCkgaXMubnVtZXJpYyhjb2wpIHwKIyAgICAgICAgICAgICAgIGlzLmludGVnZXIoY29sKQojICAgICAgICAgICAgKSAlPiUgCiMgICAgc2NhbGUoKQoKIyBzdHIoQ2x1c3REYXRhKQojIFZpZXcodGFpbChDbHVzdERhdGEsIDUwMCkpCiMgcm93bmFtZXMoQ2x1c3REYXRhKSA8LSBDbHVzdERhdGEkUm91dGUKIyBDbHVzdERhdGEkUm91dGUgPC0gYXMuZmFjdG9yKENsdXN0RGF0YSRSb3V0ZSkKIyBzdHIoQ2x1c3REYXRhKQojIGhlYWQoQ2x1c3REYXRhKQoKClJvdXRlU3RhdHMgPC0gZmlsdGVyKFdhaXRUaW1lX1J0ZUNudHMsCiAgICAgICAgICAgICAgICAgICAgIFdhaXRUaW1lX01pbjIgPD0gMTgwCiAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFNwZWVkQXZnX01waF9URE1OSF9UVFNOID0gVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycyAvIChUVF9TZWNfTmV3IC8gNjAgLyA2MCkKICAgICAgICApICU+JSAKICBncm91cF9ieShSb3V0ZSkgJT4lIAogIHN1bW1hcmlzZShCdXNEYXlFdmVudE51bV9NZWFuID0gbWVhbihCdXNEYXlfRXZlbnROdW0sIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIEJ1c0RheUV2ZW50TnVtX1BjdDEwID0gcXVhbnRpbGUoQnVzRGF5X0V2ZW50TnVtLCBwcm9icyA9IDAuMTAsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIEJ1c0RheUV2ZW50TnVtX1BjdDI1ID0gcXVhbnRpbGUoQnVzRGF5X0V2ZW50TnVtLCBwcm9icyA9IDAuMjUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIEJ1c0RheUV2ZW50TnVtX1BjdDUwID0gcXVhbnRpbGUoQnVzRGF5X0V2ZW50TnVtLCBwcm9icyA9IDAuNTAsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIEJ1c0RheUV2ZW50TnVtX1BjdDc1ID0gcXVhbnRpbGUoQnVzRGF5X0V2ZW50TnVtLCBwcm9icyA9IDAuNzUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIEJ1c0RheUV2ZW50TnVtX1BjdDkwID0gcXVhbnRpbGUoQnVzRGF5X0V2ZW50TnVtLCBwcm9icyA9IDAuOTAsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIFN0b3BTZXF1ZW5jZV9NZWFuID0gbWVhbihTdG9wX1NlcXVlbmNlLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBTdG9wU2VxdWVuY2VfUGN0MTAgPSBxdWFudGlsZShTdG9wX1NlcXVlbmNlLCBwcm9icyA9IDAuMTAsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIFN0b3BTZXF1ZW5jZV9QY3QyNSA9IHF1YW50aWxlKFN0b3BfU2VxdWVuY2UsIHByb2JzID0gMC4yNSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgU3RvcFNlcXVlbmNlX1BjdDUwID0gcXVhbnRpbGUoU3RvcF9TZXF1ZW5jZSwgcHJvYnMgPSAwLjUwLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBTdG9wU2VxdWVuY2VfUGN0NzUgPSBxdWFudGlsZShTdG9wX1NlcXVlbmNlLCBwcm9icyA9IDAuNzUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIFN0b3BTZXF1ZW5jZV9QY3Q5MCA9IHF1YW50aWxlKFN0b3BfU2VxdWVuY2UsIHByb2JzID0gMC45MCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgRXZlbnRUaW1lSHJfTWVhbiA9IG1lYW4oRXZlbnRfVGltZV9IciwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgRXZlbnRUaW1lSHJfUGN0MTAgPSBxdWFudGlsZShFdmVudF9UaW1lX0hyLCBwcm9icyA9IDAuMTAsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIEV2ZW50VGltZUhyX1BjdDI1ID0gcXVhbnRpbGUoRXZlbnRfVGltZV9IciwgcHJvYnMgPSAwLjI1LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBFdmVudFRpbWVIcl9QY3Q1MCA9IHF1YW50aWxlKEV2ZW50X1RpbWVfSHIsIHByb2JzID0gMC41MCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgRXZlbnRUaW1lSHJfUGN0NzUgPSBxdWFudGlsZShFdmVudF9UaW1lX0hyLCBwcm9icyA9IDAuNzUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIEV2ZW50VGltZUhyX1BjdDkwID0gcXVhbnRpbGUoRXZlbnRfVGltZV9IciwgcHJvYnMgPSAwLjkwLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBEd2VsbFRpbWUyX01lYW4gPSBtZWFuKER3ZWxsX1RpbWUyLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBEd2VsbFRpbWUyX1BjdDEwID0gcXVhbnRpbGUoRHdlbGxfVGltZTIsIHByb2JzID0gMC4xMCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgRHdlbGxUaW1lMl9QY3QyNSA9IHF1YW50aWxlKER3ZWxsX1RpbWUyLCBwcm9icyA9IDAuMjUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIER3ZWxsVGltZTJfUGN0NTAgPSBxdWFudGlsZShEd2VsbF9UaW1lMiwgcHJvYnMgPSAwLjUwLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBEd2VsbFRpbWUyX1BjdDc1ID0gcXVhbnRpbGUoRHdlbGxfVGltZTIsIHByb2JzID0gMC43NSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgRHdlbGxUaW1lMl9QY3Q5MCA9IHF1YW50aWxlKER3ZWxsX1RpbWUyLCBwcm9icyA9IDAuOTAsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIFRyYXZEaXN0TWlfTWVhbiA9IG1lYW4oVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgVHJhdkRpc3RNaV9QY3QxMCA9IHF1YW50aWxlKFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9icyA9IDAuMTAsIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICBUcmF2RGlzdE1pX1BjdDI1ID0gcXVhbnRpbGUoVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2JzID0gMC4yNSwgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgIFRyYXZEaXN0TWlfUGN0NTAgPSBxdWFudGlsZShUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvYnMgPSAwLjUwLCBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgVHJhdkRpc3RNaV9QY3Q3NSA9IHF1YW50aWxlKFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9icyA9IDAuNzUsIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICBUcmF2RGlzdE1pX1BjdDkwID0gcXVhbnRpbGUoVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2JzID0gMC45MCwgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgIFRyYXZUaW1TZWNfTWVhbiA9IG1lYW4oVFRfU2VjX05ldywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgVHJhdlRpbVNlY19QY3QxMCA9IHF1YW50aWxlKFRUX1NlY19OZXcsIHByb2JzID0gMC4xMCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgVHJhdlRpbVNlY19QY3QyNSA9IHF1YW50aWxlKFRUX1NlY19OZXcsIHByb2JzID0gMC4yNSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgVHJhdlRpbVNlY19QY3Q1MCA9IHF1YW50aWxlKFRUX1NlY19OZXcsIHByb2JzID0gMC41MCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgVHJhdlRpbVNlY19QY3Q3NSA9IHF1YW50aWxlKFRUX1NlY19OZXcsIHByb2JzID0gMC43NSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgVHJhdlRpbVNlY19QY3Q5MCA9IHF1YW50aWxlKFRUX1NlY19OZXcsIHByb2JzID0gMC45MCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgV2FpdFRpbU1pbl9NZWFuID0gbWVhbihXYWl0VGltZV9NaW4yLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBXYWl0VGltTWluX1BjdDEwID0gcXVhbnRpbGUoV2FpdFRpbWVfTWluMiwgcHJvYnMgPSAwLjEwLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBXYWl0VGltTWluX1BjdDI1ID0gcXVhbnRpbGUoV2FpdFRpbWVfTWluMiwgcHJvYnMgPSAwLjI1LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBXYWl0VGltTWluX1BjdDUwID0gcXVhbnRpbGUoV2FpdFRpbWVfTWluMiwgcHJvYnMgPSAwLjUwLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBXYWl0VGltTWluX1BjdDc1ID0gcXVhbnRpbGUoV2FpdFRpbWVfTWluMiwgcHJvYnMgPSAwLjc1LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBXYWl0VGltTWluX1BjdDkwID0gcXVhbnRpbGUoV2FpdFRpbWVfTWluMiwgcHJvYnMgPSAwLjkwLCBuYS5ybSA9IFRSVUUpCiAgICAgICAgICAgKSAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpCgpzdHIoUm91dGVTdGF0cykKCnJvd25hbWVzKFJvdXRlU3RhdHMpIDwtIFJvdXRlU3RhdHMkUm91dGUKc3RyKFJvdXRlU3RhdHMpClZpZXcoUm91dGVTdGF0cykKCgpSb3V0ZVN0YXRzX1NjYWxlZCA8LSBzZWxlY3QoUm91dGVTdGF0cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIC1Sb3V0ZQogICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzY2FsZSgpCgpzdHIoUm91dGVTdGF0c19TY2FsZWQpCmNsYXNzKFJvdXRlU3RhdHNfU2NhbGVkKQpWaWV3KFJvdXRlU3RhdHNfU2NhbGVkKQoKc3VtbWFyeShSb3V0ZVN0YXRzKQpzdW1tYXJ5KFJvdXRlU3RhdHNfU2NhbGVkKQoKIyA8LSBzZWxlY3RfaWYoQ2x1c3REYXRhLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKGNvbCkgaXMubnVtZXJpYyhjb2wpIHwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlzLmludGVnZXIoY29sKQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgIyBzY2FsZSgpICU+JSAKICAjIGFzLmRhdGEuZnJhbWUoKSAlPiUgCiAgIyBuYS5vbWl0KCkKCiMgc3RyKENsdXN0RGF0YV9Ob0ZhY3QpCiMgc3VtbWFyeShDbHVzdERhdGFfTm9GYWN0KQoKYGBgCgoKUENBCmBgYHtyfQoKVHJuc2ZybSA8LSBwcmVQcm9jZXNzKHNlbGVjdChSb3V0ZVN0YXRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIC1Sb3V0ZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9IGMoIkJveENveCIsICJjZW50ZXIiLCAic2NhbGUiLCAicGNhIikKICAgICAgICAgICAgICAgICAgICAgKQoKIyBsb2FkaW5ncwpUcm5zZnJtJHJvdGF0aW9uCgpSb3V0ZVN0YXRzX1BjYSA8LSBwcmVkaWN0KFRybnNmcm0sIFJvdXRlU3RhdHMpICU+JSAKICBzZWxlY3QoLVJvdXRlKQpSb3V0ZVN0YXRzX1BjYQoKYGBgCgoKQ2x1c3RlcmluZy4KCkFyZSB0aGUgZGF0YSBjbHVzdGVyYWJsZT8KYGBge3J9CgojIyMjIyBBcmUgdGhlIGRhdGEgY2x1c3RlcmFibGU/CiMgZ3JhZGllbnRfY29sIDwtIGxpc3QobG93ID0gInN0ZWVsYmx1ZSIsIGhpZ2ggPSAid2hpdGUiKQpDbHVzdERhdGFfRW5kcyA8LSBnZXRfY2x1c3RfdGVuZGVuY3koUm91dGVTdGF0c19QY2EsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuID0gbnJvdyhSb3V0ZVN0YXRzX1BjYQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApIC0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgZ3JhZGllbnQgPSBncmFkaWVudF9jb2wsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWVkID0gMTIzNDU2Nzg5CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCnN0cihDbHVzdERhdGFfRW5kcykKCiMgSG9wa2lucyBzdGF0aXN0aWMKQ2x1c3REYXRhX0VuZHMkaG9wa2luc19zdGF0ICAjIHZhbHVlIG9mIDAuMTY1NzQ5NCBpbXBsaWVzIHRoYXQgdGhlIGRhdGEgYXJlIG5vdCB1bmlmb3JtbHkgZGlzdHJpYnV0ZWQgKHRoZXkgYXJlICJjbHVzdGVyYWJsZSIpCgojcGxvdApDbHVzdERhdGFfRW5kcyRwbG90CgpgYGAKCgpDbHVzdGVyaW5nLiBIb3cgbWFueSBjbHVzdGVycyBhcmUgdGhlcmU/CgprbWVhbnMsIHBhbSwgYW5kIGhpZXJhcmNoaWNhbCBjbHVzdHJpbmcgbWV0aG9kcywgdXNpbmcgd2l0aGluIHN1bSBvZiBzcXVhcmVzIGFuZCBzaWxob3VldHRlIG1lYXN1cmVzLgpgYGB7cn0KCiMgY2xhc3MoUm91dGVTdGF0c19QY2EpCgpmdml6X25iY2x1c3QoUm91dGVTdGF0c19QY2EsIGttZWFucywgbWV0aG9kID0gIndzcyIpICAjIH44IGNsdXN0ZXJzCmZ2aXpfbmJjbHVzdChSb3V0ZVN0YXRzX1BjYSwgcGFtLCBtZXRob2QgPSAid3NzIikgICMgfjYgY2x1c3RlcnMKZnZpel9uYmNsdXN0KFJvdXRlU3RhdHNfUGNhLCBoY3V0LCBtZXRob2QgPSAid3NzIikgICMgfjYgY2x1c3RlcnMKCmZ2aXpfbmJjbHVzdChSb3V0ZVN0YXRzX1BjYSwga21lYW5zLCBtZXRob2QgPSAic2lsaG91ZXR0ZSIpICAjIDIgY2x1c3RlcnMKZnZpel9uYmNsdXN0KFJvdXRlU3RhdHNfUGNhLCBwYW0sIG1ldGhvZCA9ICJzaWxob3VldHRlIikgICMgMiBjbHVzdGVycwpmdml6X25iY2x1c3QoUm91dGVTdGF0c19QY2EsIGhjdXQsIG1ldGhvZCA9ICJzaWxob3VldHRlIiwKICAgICAgICAgICAgIGhjX21ldGhvZCA9ICJjb21wbGV0ZSIpICAjIDIgY2x1c3RlcnMKCmBgYAoKCkNsdXN0ZXJpbmcuIEhvdyBtYW55IGNsdXN0ZXJzIGFyZSB0aGVyZT8KCmttZWFucyBtZXRob2Qgd2l0aCB0aGUgZ2FwIHN0YXRpc3RpYywgdXNpbmcgYm9vdHN0cmFwLgpgYGB7cn0KCiMgQ29tcHV0ZSBnYXAgc3RhdGlzdGljCiMga21lYW5zIHZlcnNpb24Kc2V0LnNlZWQoMTIzNDU2Nzg5KQojIHN5c3RlbS50aW1lKApnYXBfc3RhdF9rbSA8LSBjbHVzR2FwKFJvdXRlU3RhdHNfUGNhLAogICAgICAgICAgICAgICAgICAgICAgIEZVTiA9IGttZWFucywKICAgICAgICAgICAgICAgICAgICAgICBuc3RhcnQgPSAyNSwKICAgICAgICAgICAgICAgICAgICAgICBLLm1heCA9IDEwLAogICAgICAgICAgICAgICAgICAgICAgIEIgPSA1MDAKICAgICAgICAgICAgICAgICAgICAgICkKIyApCgojIFByaW50CnByaW50KGdhcF9zdGF0X2ttLCBtZXRob2QgPSAiVGliczIwMDFTRW1heCIpCnByaW50KGdhcF9zdGF0X2ttKQoKCiMgcGFtIHZlcnNpb24Kc2V0LnNlZWQoMTIzNDU2Nzg5KQpnYXBfc3RhdF9wbSA8LSBjbHVzR2FwKFJvdXRlU3RhdHNfUGNhLAogICAgICAgICAgICAgICAgICAgICAgIEZVTiA9IHBhbSwKICAgICAgICAgICAgICAgICAgICAgICBLLm1heCA9IDEwLAogICAgICAgICAgICAgICAgICAgICAgIEIgPSA1MDAKICAgICAgICAgICAgICAgICAgICAgICkKCiMgUHJpbnQKcHJpbnQoZ2FwX3N0YXRfcG0sIG1ldGhvZCA9ICJUaWJzMjAwMVNFbWF4IikKcHJpbnQoZ2FwX3N0YXRfcG0pCgoKIyBoaWVyYXJjaGljYWwgdmVyc2lvbgpzZXQuc2VlZCgxMjM0NTY3ODkpCmdhcF9zdGF0X2hjdXQgPC0gY2x1c0dhcChSb3V0ZVN0YXRzX1BjYSwKICAgICAgICAgICAgICAgICAgICAgICAgIEZVTiA9IGhjdXQsCiAgICAgICAgICAgICAgICAgICAgICAgICBLLm1heCA9IDEwLAogICAgICAgICAgICAgICAgICAgICAgICAgQiA9IDUwMAogICAgICAgICAgICAgICAgICAgICAgICApCgojIFByaW50CnByaW50KGdhcF9zdGF0X2hjdXQsIG1ldGhvZCA9ICJUaWJzMjAwMVNFbWF4IikKcHJpbnQoZ2FwX3N0YXRfaGN1dCkKCgoKIyBQbG90IGttZWFucwpmdml6X2dhcF9zdGF0KGdhcF9zdGF0X2ttLCAKICAgICAgICAgICAgICBtYXhTRSA9IGxpc3QobWV0aG9kID0gIlRpYnMyMDAxU0VtYXgiKQogICAgICAgICAgICAgKSAgIyAxIGNsdXN0ZXIKCiMgUGxvdCBwYW0KZnZpel9nYXBfc3RhdChnYXBfc3RhdF9wbSwgCiAgICAgICAgICAgICAgbWF4U0UgPSBsaXN0KG1ldGhvZCA9ICJUaWJzMjAwMVNFbWF4IikKICAgICAgICAgICAgICkgICMgMiBjbHVzdGVyCgojIFBsb3QgaGllcmFyY2hpY2FsCmZ2aXpfZ2FwX3N0YXQoZ2FwX3N0YXRfaGN1dCwgCiAgICAgICAgICAgICAgbWF4U0UgPSBsaXN0KG1ldGhvZCA9ICJUaWJzMjAwMVNFbWF4IikKICAgICAgICAgICAgICkgICMgMSBjbHVzdGVyCgpgYGAKCgpDbHVzdGVyaW5nLiBIb3cgbWFueSBjbHVzdGVycyBhcmUgdGhlcmU/CgprbWVhbnMgbWV0aG9kIHdpdGggdmFyaW91cyBkaWZmZXJlbnQgc3RhdGlzdGljcy4KYGBge3J9CgojIHN0cihpcmlzKQoKbmIgPC0gTmJDbHVzdChSb3V0ZVN0YXRzX1BjYSwgI3NjYWxlKGlyaXNbICwtNV0pLAogICAgICAgICAgICAgIGRpc3RhbmNlID0gImV1Y2xpZGVhbiIsCiAgICAgICAgICAgICAgbWluLm5jID0gMiwKICAgICAgICAgICAgICBtYXgubmMgPSAxNSwKICAgICAgICAgICAgICBtZXRob2QgPSAia21lYW5zIiwKICAgICAgICAgICAgICBpbmRleCA9ICJhbGwiCiAgICAgICAgICAgICApCgpmdml6X25iY2x1c3QobmIpICsgdGhlbWVfbWluaW1hbCgpCgpgYGAKCgpDbHVzdGVyaW5nLiBIb3cgbWFueSBjbHVzdGVycyBhcmUgdGhlcmU/CgpIaWVyYXJjaGljYWwgY2x1c3RlcmluZyBtZXRob2QuIFBhcnRpY3VsYXJseSBsb29raW5nIGF0IHNpbGhvdWV0dGUgc3RhdGlzdGljcy4KYGBge3J9CgojIEhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nLCBjdXQgaW4gMiB0byAxNSBncm91cHMKZm9yKGkgaW4gMjoxNSkgewogIGFzc2lnbihwYXN0ZTAoIkhDUmVzX0siLCBpKSwKICAgICAgICAgZWNsdXN0KFJvdXRlU3RhdHNfUGNhLAogICAgICAgICAgICAgICAgImhjbHVzdCIsCiAgICAgICAgICAgICAgICBrID0gaSwKICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJjb21wbGV0ZSIsCiAgICAgICAgICAgICAgICBncmFwaCA9IEZBTFNFCiAgICAgICAgICAgICAgICkKICAgICAgICApCiAgCiAgYXNzaWduKCJ4IiwKICAgICAgICAgZ2V0KHBhc3RlMCgiSENSZXNfSyIsIGkpCiAgICAgICAgICAgICkKICAgICAgICApCiAgCiAgYXNzaWduKHBhc3RlMCgiSENTdGF0c19LIiwgaSksCiAgICAgICAgIGNsdXN0ZXIuc3RhdHMoZGlzdChSb3V0ZVN0YXRzX1NjYWxlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ImV1Y2xpZGVhbiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICB4JGNsdXN0ZXIKICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICApCiAgCiAgYXNzaWduKCJ5IiwKICAgICAgICAgZ2V0KHBhc3RlMCgiSENTdGF0c19LIiwgaSkKICAgICAgICAgICAgKQogICAgICAgICkKICAKICBhc3NpZ24ocGFzdGUwKCJIQ0RlbmRfSyIsIGkpLAogICAgICAgICBmdml6X2RlbmQoeCwgcmVjdCA9IFRSVUUsIHNob3dfbGFiZWxzID0gRkFMU0UpCiAgICAgICAgKQogIAogIGFzc2lnbihwYXN0ZTAoIkhDU2lsX0siLCBpKSwKICAgICAgICAgZnZpel9zaWxob3VldHRlKHgpCiAgICAgICAgKQogIAogIGFzc2lnbihwYXN0ZTAoIkhDU2lsV2lkdGhfSyIsIGkpLAogICAgICAgICBhcy5kYXRhLmZyYW1lKHkkY2x1cy5hdmcuc2lsd2lkdGhzKSAlPiUgCiAgICAgICAgICAgbXV0YXRlKEtWYWwgPSAxOm5yb3coLikKICAgICAgICAgICAgICAgICApCiAgICAgICAgKQogIH0KCgpIQ1NpbFdpZHRoX0FsbEsgPC0gbGVmdF9qb2luKHNlbGVjdChIQ1NpbFdpZHRoX0sxNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgS1ZhbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYHkkY2x1cy5hdmcuc2lsd2lkdGhzYAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSENTaWxXaWR0aF9LMTQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIGxlZnRfam9pbiguLAogICAgICAgICAgICBIQ1NpbFdpZHRoX0sxMywKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApICU+JSAKICBsZWZ0X2pvaW4oLiwKICAgICAgICAgICAgSENTaWxXaWR0aF9LMTIsCiAgICAgICAgICAgIGJ5ID0gYygiS1ZhbCIgPSAiS1ZhbCIpCiAgICAgICAgICAgKSAlPiUgCiAgbGVmdF9qb2luKC4sCiAgICAgICAgICAgIEhDU2lsV2lkdGhfSzExLAogICAgICAgICAgICBieSA9IGMoIktWYWwiID0gIktWYWwiKQogICAgICAgICAgICkgJT4lIAogIGxlZnRfam9pbiguLAogICAgICAgICAgICBIQ1NpbFdpZHRoX0sxMCwKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApICU+JSAKICBsZWZ0X2pvaW4oLiwKICAgICAgICAgICAgSENTaWxXaWR0aF9LOSwKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApICU+JSAKICBsZWZ0X2pvaW4oLiwKICAgICAgICAgICAgSENTaWxXaWR0aF9LOCwKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApICU+JSAKICBsZWZ0X2pvaW4oLiwKICAgICAgICAgICAgSENTaWxXaWR0aF9LNywKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApICU+JSAKICBsZWZ0X2pvaW4oLiwKICAgICAgICAgICAgSENTaWxXaWR0aF9LNiwKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApICU+JSAKICBsZWZ0X2pvaW4oLiwKICAgICAgICAgICAgSENTaWxXaWR0aF9LNSwKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApICU+JSAKICBsZWZ0X2pvaW4oLiwKICAgICAgICAgICAgSENTaWxXaWR0aF9LNCwKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApICU+JSAKICBsZWZ0X2pvaW4oLiwKICAgICAgICAgICAgSENTaWxXaWR0aF9LMywKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApICU+JSAKICBsZWZ0X2pvaW4oLiwKICAgICAgICAgICAgSENTaWxXaWR0aF9LMiwKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApCiAgCmNvbG5hbWVzKEhDU2lsV2lkdGhfQWxsSykgPC0gYygiS1ZhbCIsICJLMTUiLCAiSzE0IiwgIksxMyIsICJLMTIiLCAiSzExIiwgIksxMCIsICJLOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSzgiLCAiSzciLCAiSzYiLCAiSzUiLCAiSzQiLCAiSzMiLCAiSzIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCgojIFZpc3VhbGl6ZQpIQ0RlbmRfSzIKSENEZW5kX0szCkhDRGVuZF9LNApIQ0RlbmRfSzUKSENEZW5kX0s2CkhDRGVuZF9LNwpIQ0RlbmRfSzgKSENEZW5kX0s5CkhDRGVuZF9LMTAKSENEZW5kX0sxMQpIQ0RlbmRfSzEyCkhDRGVuZF9LMTMKSENEZW5kX0sxNApIQ0RlbmRfSzE1CgpIQ1NpbF9LMgpIQ1NpbF9LMwpIQ1NpbF9LNApIQ1NpbF9LNQpIQ1NpbF9LNgpIQ1NpbF9LNwpIQ1NpbF9LOApIQ1NpbF9LOQpIQ1NpbF9LMTAKSENTaWxfSzExCkhDU2lsX0sxMgpIQ1NpbF9LMTMKSENTaWxfSzE0CkhDU2lsX0sxNQoKCkhDU2lsV2lkdGhfQWxsSwoKYGBgCgoKVXNpbmcga21lYW5zLCBQQU0sIGFuZCBIaWVyYXJjaGljYWwgY2x1c3RlcmluZyBtZXRob2RzLCB3ZSBjYW4gc2F5IHdlIHByb2JhYmx5IGhhdmUgYXJvdW4gMiBjbHVzdGVycy4KCkxldCdzIHRyeSBkZW5zaXR5IGNsdXN0ZXJpbmcuICAoVGhpcyB0ZW5kcyB0byBzaG93IHRoYXQgbWF5YmUgdGhlcmUgaXMgb25seSBvbmUgImNsdXN0ZXIsIiBtZWFuaW5nIHRoYXQgZGF0YSBhcmUgbm90IGNsdXN0ZXJhYmxlLikKYGBge3J9CgpybShsaXN0ID0gbHMocGF0dGVybiA9ICJfSyIpCiAgKQoKCiMgQ29tcHV0ZSBEQlNDQU4gdXNpbmcgZnBjIHBhY2thZ2UKa05OZGlzdHBsb3QoUm91dGVTdGF0c19QY2EsIGsgPSAxMCkKYWJsaW5lKGggPSA4LjUsIGx0eSA9IDIpCgpzZXQuc2VlZCgxMjM0NTY3ODkpCmRiIDwtIGZwYzo6ZGJzY2FuKFJvdXRlU3RhdHNfUGNhLAogICAgICAgICAgICAgICAgICBlcHMgPSA4LjUsCiAgICAgICAgICAgICAgICAgIE1pblB0cyA9IDEwCiAgICAgICAgICAgICAgICApCgpzdHIoZGIpCmRiCgojIFBsb3QgREJTQ0FOIHJlc3VsdHMKZnZpel9jbHVzdGVyKGRiLAogICAgICAgICAgICAgUm91dGVTdGF0c19QY2EsCiAgICAgICAgICAgICBzdGFuZCA9IEZBTFNFLAogICAgICAgICAgICAgZnJhbWUgPSBGQUxTRSwKICAgICAgICAgICAgIGdlb20gPSAicG9pbnQiCiAgICAgICAgICAgICkKCmBgYAoKCgoKSW52ZXN0aWdhdGluZyBUcmF2ZWxUaW1lX1NlYy4KYGBge3J9CgpWaWV3KGZpbHRlcihUVExhcmdlUnRlQ2huZywKICAgICAgICAgICAgIWlzLm5hKFRyYXZlbFRpbWVfU2VjKSAmCiAgICAgICAgICAgICAgUnRlQ2hhbmdlMiA9PSAiU2FtZSIKICAgICAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoZGVzYyhUcmF2ZWxUaW1lX1NlYyksCiAgICAgICAgICAgICAgIFNwZWVkQXZnX01waF9OZXdIdnJzCiAgICAgICAgICAgICAgKSAlPiUKICAgICAgIGhlYWQoNTAwKQogICAgKQoKCiMgZXhhbXBsZXMgd2hlcmUgVHJhdmVsVGltZV9TZWMgaXMgc21hbGwgKDEgc2VjKSBhbmQgU3BlZWRBdmdfTXBoX05ld0h2cnMgaXMgbGFyZ2UuClZpZXcoc2VsZWN0KE5ld1RyYXZUaW1lLAogICAgICAgICAgICAjIC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICAgLShURF9NaV9xMjpURF9NaV9TU0hHX0NudF9GKSwKICAgICAgICAgICAgLShUVF9Icl9xMjpUVF9Icl9TU0hHX0NudF9GKQogICAgICAgICAgICkgJT4lIAogICAgICAgZmlsdGVyKChSb3dOdW1fT0cgPj0gMjIxNzM1MyAmIFJvd051bV9PRyA8PSAyMjE3MzczKSB8ICMgMjIxNzM2MwogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAzMDkwMzIxICYgUm93TnVtX09HIDw9IDMwOTAzNDEpIHwgIyAzMDkwMzMxCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDgwNzY0ICYgUm93TnVtX09HIDw9IDgwNzg0KSB8ICMgODA3NzQKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMzM4NDAgJiBSb3dOdW1fT0cgPD0gMzM4NjApICMgMzM4NTAKICAgICAgICAgICApCiAgICApCgoKCgoKCiMgZXhhbXBsZXMgd2hlcmUgVHJhdmVsVGltZV9TZWMgaXMgbGFyZ2UgYW5kIFNwZWVkQXZnX01waF9OZXdIdnJzIGlzIHNtYWxsLgpWaWV3KGZpbHRlcihUVExhcmdlUnRlQ2huZywKICAgICAgICAgICAgKFJvd051bV9PRyA+PSAyMjUwMjkwICYgUm93TnVtX09HIDw9IDIyNTAzMTApIHwgIyAyMjUwMzAwCiAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSA4Njc3MTcgJiBSb3dOdW1fT0cgPD0gODY3NzM3KSB8ICMgODY3NzI3CiAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSA4NjQzNzkgJiBSb3dOdW1fT0cgPD0gODY0Mzk5KSB8ICMgODY0Mzg5CiAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSA4MDgzOTUgJiBSb3dOdW1fT0cgPD0gODA4NDE1KSAjIDgwODQwNQogICAgICAgICAgICkKICAgICkKYGBgCgoKCgpgYGB7cn0KCiAgICAgICAgIAogICAgICAgICAKIyBleGFtcGxlcyB3aGVyZSBUcmF2ZWxUaW1lX1NlYyBpcyB1bnVzdWFsbHkgc21hbGwgKHdpdGggVHJhdmVsRGlzdGFuY2VfTWkgdmFsdWVzIHRoYXQgYXJlIGxhcmdlKS4KVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAoUm93TnVtX09HID49IDEwNDIyMjggJiBSb3dOdW1fT0cgPD0gMTA0MjI0OCkgfCAjIDEwNDIyMzgKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gNTM4MTYgJiBSb3dOdW1fT0cgPD0gNTM4MzYpIHwgIyA1MzgyNgogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAzNjA1NzEgJiBSb3dOdW1fT0cgPD0gMzYwNTkxKSB8ICMgMzYwNTgxCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDUwMjI3MSAmIFJvd051bV9PRyA8PSA1MDIyOTEpICMgNTAyMjgxIChjYW4ndCBleHBsaWFuIHRoZSB3ZWlyZCBUcmF2ZWxUaW1lX1NlYyBjYWxjdWxhdGlvbiBoZXJlIC0gaXQncyBub3QgZXZlbiBhbiBpbnRlZ2VyISkKICAgICAgICAgICApCiAgICApCgojIHN0aWxsIHRyeWluZyB0byBleHBsYWluIDUwMjI4MS4uLm9uIHRoZSBkYXkgb2YgdGhpcyB3ZWlyZG5lc3MsIHRoZSBidXMgd2FzIG9ubHkgaW4gY2lyY3VsYXRpb24gZm9yIDQtNSBzdG9wcyAofjIwIG1pbnV0ZXMpIG9uIHRoYXQgZGF5IChPY3QgNikKVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICBCdXNfSUQgPT0gMjcxMQogICAgICAgICAgICkKICAgICkKCgojIGV4cGxvcmluZyBsYXJnZSB2YWx1ZXMgZm9yIFRyYXZlbFRpbWVfU2VjClZpZXcoZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgVHJhdmVsVGltZV9TZWMgPT0gMzAwCiAgICAgICAgICAgKSAlPiUgCiAgICAgICBhcnJhbmdlKGRlc2MoVHJhdmVsVGltZV9TZWMpLAogICAgICAgICAgICAgICBTcGVlZEF2Z19NcGgyCiAgICAgICAgICAgICAgKQogICAgKQoKIyBleGFtcGxlcyB3aGVyZSBUcmF2ZWxUaW1lX1NlYyBpcyB1bnVzdWFsbHkgbGFyZ2UgKHdpdGggVHJhdmVsRGlzdGFuY2VfTWkgdmFsdWVzIHRoYXQgYXJlIHNtYWxsLCBzbyBTcGVlZEF2Z19NcGggdmFsdWVzIGFyZSB2ZXJ5IHNtYWxsKS4KVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAoUm93TnVtX09HID49IDI2Mjc0NTkgJiBSb3dOdW1fT0cgPD0gMjYyNzQ3OSkgfCAjIDI2Mjc0NjkKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMjE5MzM0NCAmIFJvd051bV9PRyA8PSAyMTkzMzY0KSB8ICMgMjE5MzM1NAogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAxNjQ0MTIzICYgUm93TnVtX09HIDw9IDE2NDQxNDMpIHwgIyAxNjQ0MTMzCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDg2OTYwMCAmIFJvd051bV9PRyA8PSA4Njk2MjApICMgODY5NjEwCiAgICAgICAgICAgKQogICAgKQoKYGBgCgpJbnZlc3RpZ2F0aW9uIG9mIFNwZWVkQXZnX01waDIKClZpZXcoU3BlZWRfUGN0aWxlcyk6IDkwJSBvZiBTcGVlZEF2Z19NcGgyIGFyZSBiZXR3ZWVuIH4zbXBoIGFuZCB+NjZtcGguCmBgYHtyfQoKU3BlZWRfTnRpbGUgPC0gYXMuZGF0YS5mcmFtZShBbGxEYXlzX05ld1RyYXZlbERpc3QkU3BlZWRBdmdfTXBoMikgJT4lIAogIG11dGF0ZShQY3RpbGUgPSBudGlsZShBbGxEYXlzX05ld1RyYXZlbERpc3QkU3BlZWRBdmdfTXBoMiwgMTAwKSwKICAgICAgICAgTWluUiA9IG1pbl9yYW5rKEFsbERheXNfTmV3VHJhdmVsRGlzdCRTcGVlZEF2Z19NcGgyKSwKICAgICAgICAgUGN0UiA9IHBlcmNlbnRfcmFuayhBbGxEYXlzX05ld1RyYXZlbERpc3QkU3BlZWRBdmdfTXBoMiksCiAgICAgICAgIFBjdFJfUm91bmQgPSByb3VuZChQY3RSLCAyKQogICAgICAgICkgCgpjb2xuYW1lcyhTcGVlZF9OdGlsZSlbMV0gPC0gIlNwZWVkQXZnX01waDIiCnN0cihTcGVlZF9OdGlsZSkKClNwZWVkX050aWxlX1Jvd3MgPC0gbnJvdyhTcGVlZF9OdGlsZSkKClZpZXcodGFpbChTcGVlZF9OdGlsZSwgNTAwKSkKCgpTcGVlZF9QY3RpbGVzIDwtIGdyb3VwX2J5KFNwZWVkX050aWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgIFBjdFJfUm91bmQKICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHN1bW1hcmlzZSgKICAgIE1pblNwZWVkQXRQY3RpbGUgPSBtaW4oU3BlZWRBdmdfTXBoMiksCiAgICBDbnRzQXRQY3RpbGUgPSBuKCksCiAgICBQY3RzQXRQY3RpbGUgPSBDbnRzQXRQY3RpbGUgLyBTcGVlZF9OdGlsZV9Sb3dzCiAgKSAlPiUgCiAgbXV0YXRlKEN1bVN1bVBBdFAgPSBjdW1zdW0oUGN0c0F0UGN0aWxlKQogICAgICAgICkKClZpZXcoU3BlZWRfUGN0aWxlcykKCmBgYAoKSW52ZXN0aWdhdGlvbiBvZiBTcGVlZEF2Z19NcGgyLgoKRXhwbG9yaW5nIHRoZSByZW1vdmFsIG9mIG91dGxpZXIgVHJhdmVsVGltZV9TZWMgYW5kIFRyYXZlbERpc3RhbmNlX01pLgpgYGB7cn0KCnN1bW1hcnkoc2VsZWN0KEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgU3BlZWRBdmdfTXBoLAogICAgICAgICAgICAgICBTcGVlZEF2Z19NcGgyCiAgICAgICAgICAgICAgKQogICAgICAgKQoKc3VtbWFyeShzZWxlY3QoZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pID4gMC4wMDAxODkzOTM5ICYgIyBsb3dlc3Qgbm9uLXplcm8gcGVyY2VudGlsZQogICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaSA8IDEuMDgxMjUwMDAwMCAmICMgOTl0aCBwZXJjZW50aWxlCiAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjID4gMTAuMDUwMDAwICYgIyAybmQgcGVyY2VudGlsZQogICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYyA8IDI5My4wMDAwMDAgIyA5OHRoIHBlcmNlbnRpbGUKICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgU3BlZWRBdmdfTXBoLAogICAgICAgICAgICAgICBTcGVlZEF2Z19NcGgyCiAgICAgICAgICAgICAgKQogICAgICAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBTcGVlZEF2Z19NcGgyLgoKSGlzdG9ncmFtIG9mIFNwZWVkQXZnX01waDIuCmBgYHtyfQoKU3BlZWRfSGlzdERlbiA8LSBnZ3Bsb3QoZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICFpcy5uYShTcGVlZEF2Z19NcGgyKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IFNwZWVkQXZnX01waDIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gLi5kZW5zaXR5Li4KICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNSwgZmlsbCA9ICJsaWdodGJsdWUiLCBjb2xvdXIgPSAiZ3JleTYwIiwgc2l6ZSA9IDAuMikgKwogIGdlb21fbGluZShzdGF0ID0gImRlbnNpdHkiLCBjb2xvdXIgPSAicmVkIikgKwogIHN0YXRfYmluKGJpbndpZHRoID0gNSwKICAgICAgICAgICBnZW9tID0gInRleHQiLAogICAgICAgICAgIHNpemUgPSAyLjUsCiAgICAgICAgICAgdmp1c3QgPSAxLjUsCiAgICAgICAgICAgYWVzKGxhYmVsID0gZm9ybWF0KC4uY291bnQuLiwgYmlnLm1hcmsgPSAiLCIpCiAgICAgICAgICAgICAgKSwKICAgICAgICAgICkgKwogICMgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGZvcm1hdCguLmNvdW50Li4sIGJpZy5tYXJrID0gIiwiKQogICMgICAgICAgICAgICAgICksCiAgIyAgICAgICAgICAgc2l6ZSA9IDMsCiAgIyAgICAgICAgICAgbnVkZ2VfeSA9ICguLmNvdW50Li4gKiAwLjEpCiAgIyAgICAgICAgICApICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwgNzApLCB5bGltID0gYygwLCAwLjA0KQogICAgICAgICAgICAgICAgICkgKwogICMgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsKICBsYWJzKHRpdGxlID0gIlZhcmlhdGlvbiBpbiBUcmF2ZWwgU3BlZWQiLAogICAgICAgeCA9ICJBdmVyYWdlIFNwZWVkIChtcGgpIiwKICAgICAgIHkgPSAiRGVuc2l0eSIKICAgICAgKQoKU3BlZWRfSGlzdERlbgoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBTcGVlZEF2Z19NcGgyLgoKSGlzdG9ncmFtIG9mIFNwZWVkQXZnX01waDIgYWZ0ZXIgcmVtb3Zpbmcgb3V0bGllciBUcmF2ZWxUaW1lX1NlYyBhbmQgVHJhdmVsRGlzdGFuY2VfTWkuCmBgYHtyfQoKVmlldyhUcmF2RGlzdE1pTmV3X1BjdGlsZXMpClZpZXcoVHJhdlRpbWVIcl9QY3RpbGVzKQoKU3BlZWROb091dGxpZXJfSGlzdERlbiA8LSBnZ3Bsb3QoZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICFpcy5uYShTcGVlZEF2Z19NcGgyKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ldyA+IDAuMDc3ODQxMDA1ICYgIyA1dGggcGVyY2VudGlsZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFRyYXZlbERpc3RhbmNlX01pX05ldyA8IDEuMDgxMjUwMDAwMCAmICMgOTl0aCBwZXJjZW50aWxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjID4gMTIuMTAwMDAwICMgNHRoIHBlcmNlbnRpbGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUcmF2ZWxUaW1lX1NlYyA8IDI5My4wMDAwMDAgIyA5OHRoIHBlcmNlbnRpbGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBTcGVlZEF2Z19NcGgyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IC4uZGVuc2l0eS4uCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDUsIGZpbGwgPSAibGlnaHRibHVlIiwgY29sb3VyID0gImdyZXk2MCIsIHNpemUgPSAwLjIpICsKICBnZW9tX2xpbmUoc3RhdCA9ICJkZW5zaXR5IiwgY29sb3VyID0gInJlZCIpICsKICBzdGF0X2JpbihiaW53aWR0aCA9IDUsCiAgICAgICAgICAgZ2VvbSA9ICJ0ZXh0IiwKICAgICAgICAgICBzaXplID0gMi41LAogICAgICAgICAgIHZqdXN0ID0gMS41LAogICAgICAgICAgIGFlcyhsYWJlbCA9IGZvcm1hdCguLmNvdW50Li4sIGJpZy5tYXJrID0gIiwiKQogICAgICAgICAgICAgICksCiAgICAgICAgICApICsKICAjIGdlb21fdGV4dChhZXMobGFiZWwgPSBmb3JtYXQoLi5jb3VudC4uLCBiaWcubWFyayA9ICIsIikKICAjICAgICAgICAgICAgICApLAogICMgICAgICAgICAgIHNpemUgPSAzLAogICMgICAgICAgICAgIG51ZGdlX3kgPSAoLi5jb3VudC4uICogMC4xKQogICMgICAgICAgICAgKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDAsIDcwKSwgeWxpbSA9IGMoMCwgMC4wNCkKICAgICAgICAgICAgICAgICApICsKICAjICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArCiAgbGFicyh0aXRsZSA9ICJWYXJpYXRpb24gaW4gVHJhdmVsIFNwZWVkIiwKICAgICAgIHN1YnRpdGxlID0gIihyZW1vdmVkIGxvdyBvdXRsaWVycyBvZiBUcmF2ZWwgRGlzdGFuY2UgYW5kIFRyYXZlbCBUaW1lKSIsCiAgICAgICB4ID0gIkF2ZXJhZ2UgU3BlZWQgKG1waCkiLAogICAgICAgeSA9ICJEZW5zaXR5IgogICAgICApCgpTcGVlZE5vT3V0bGllcl9IaXN0RGVuCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFNwZWVkQXZnX01waDIuCgpOZXcgZGF0YXNldCAoTm9PdXRsaWVyc19UcmF2ZWxEaXN0TlRpbWUpIHdoZW4gcmVtb3Zpbmcgb3V0bGllciBsb3cgdmFsdWVzIG9mIFRyYXZlbERpc3RhbmNlX01pX05ldyBhbmQgVHJhdmVsVGltZV9TZWMuCmBgYHtyfQoKVmlldyhUcmF2RGlzdE1pTmV3X1BjdGlsZXMpClZpZXcoVHJhdlRpbWVIcl9QY3RpbGVzKQoKTm9PdXRsaWVyc19UcmF2ZWxEaXN0TlRpbWUgPC0gZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ldyA+IC4wNzc4NDEwMDUgJiAjIDV0aCBwZXJjZW50aWxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgVHJhdmVsRGlzdGFuY2VfTWlfTmV3IDwgMS4wODEyNTAwMDAwICYgIyA5OXRoIHBlcmNlbnRpbGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsVGltZV9TZWMgPiAxMi4xMDAwMDAgIyA0dGggcGVyY2VudGlsZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFRyYXZlbFRpbWVfU2VjIDwgMjkzLjAwMDAwMCAjIDk4dGggcGVyY2VudGlsZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgpucm93KEFsbERheXNfTmV3VHJhdmVsRGlzdCkgLSBucm93KE5vT3V0bGllcnNfVHJhdmVsRGlzdE5UaW1lKQoKc3RyKE5vT3V0bGllcnNfVHJhdmVsRGlzdE5UaW1lKQpzdW1tYXJ5KE5vT3V0bGllcnNfVHJhdmVsRGlzdE5UaW1lKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBTcHBlZEF2Z19NcGgyLgoKVmlldyhTcGVlZF9Ob091dF9QY3RpbGVzKTogIEFwcm94aW1hdGVseSA5MCUgb2YgU3BlZWRBdmdfTXBoMiB2YWx1ZXMgYXJlIGJldHdlZW4gfjRtcGggYW5kIH41Nm1waC4KYGBge3J9CgpTcGVlZF9Ob091dF9OdGlsZSA8LSBhcy5kYXRhLmZyYW1lKE5vT3V0bGllcnNfVHJhdmVsRGlzdE5UaW1lJFNwZWVkQXZnX01waDIpICU+JSAKICBtdXRhdGUoUGN0aWxlID0gbnRpbGUoTm9PdXRsaWVyc19UcmF2ZWxEaXN0TlRpbWUkU3BlZWRBdmdfTXBoMiwgMTAwKSwKICAgICAgICAgTWluUiA9IG1pbl9yYW5rKE5vT3V0bGllcnNfVHJhdmVsRGlzdE5UaW1lJFNwZWVkQXZnX01waDIpLAogICAgICAgICBQY3RSID0gcGVyY2VudF9yYW5rKE5vT3V0bGllcnNfVHJhdmVsRGlzdE5UaW1lJFNwZWVkQXZnX01waDIpLAogICAgICAgICBQY3RSX1JvdW5kID0gcm91bmQoUGN0UiwgMikKICAgICAgICApIAoKY29sbmFtZXMoU3BlZWRfTm9PdXRfTnRpbGUpWzFdIDwtICJTcGVlZEF2Z19NcGgyIgpzdHIoU3BlZWRfTm9PdXRfTnRpbGUpCgpTcGVlZF9Ob091dF9OdGlsZV9Sb3dzIDwtIG5yb3coU3BlZWRfTm9PdXRfTnRpbGUpCgpWaWV3KHRhaWwoU3BlZWRfTm9PdXRfTnRpbGUsIDUwMCkpCgoKU3BlZWRfTm9PdXRfUGN0aWxlcyA8LSBncm91cF9ieShTcGVlZF9Ob091dF9OdGlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQY3RSX1JvdW5kCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzdW1tYXJpc2UoCiAgICBNaW5TcGVlZEF0UGN0aWxlID0gbWluKFNwZWVkQXZnX01waDIpLAogICAgQ250c0F0UGN0aWxlID0gbigpLAogICAgUGN0c0F0UGN0aWxlID0gQ250c0F0UGN0aWxlIC8gU3BlZWRfTm9PdXRfTnRpbGVfUm93cwogICkgJT4lIAogIG11dGF0ZShDdW1TdW1QQXRQID0gY3Vtc3VtKFBjdHNBdFBjdGlsZSkKICAgICAgICApCgpWaWV3KFNwZWVkX05vT3V0X1BjdGlsZXMpCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFNwcGVkQXZnX01waDIuCgpFeGxvcmluZyBvZGQvaW1wb3NzaWJsZSB2YWx1ZXMuCmBgYHtyfQoKIyBFeHBsb3Jpbmcgd2hlbiBTcGVlZEF2Z19NcGgyIGlzIE5BICAtLSAgZG9lcyBub3Qgb2NjdXIgYXQgYWxsCm5yb3coZmlsdGVyKE5vT3V0bGllcnNfVHJhdmVsRGlzdE5UaW1lLAogICAgICAgICAgICBpcy5uYShTcGVlZEF2Z19NcGgyKQogICAgICAgICAgICkKICAgICkKCgojIEV4cGxvcmluZyB3aGVuIFNwZWVkQXZnX01waDIgaXMgemVybyAgLS0gIGRvZXMgbm90IG9jY3VyIGF0IGFsbApucm93KGZpbHRlcihOb091dGxpZXJzX1RyYXZlbERpc3ROVGltZSwKICAgICAgICAgICAgU3BlZWRBdmdfTXBoMiA9PSAwCiAgICAgICAgICAgKQogICAgKQoKCiMgZXhhbXBsZXMgd2hlcmUgU3BlZWRBdmdfTXBoMiA8IDMuMjg0ODc3MApWaWV3KGZpbHRlcihBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgIFNwZWVkQXZnX01waDIgPiAwICYKICAgICAgICAgICAgICBTcGVlZEF2Z19NcGgyIDwgMy4yODQ4NzcwCiAgICAgICAgICAgKSAlPiUgCiAgICAgICBhcnJhbmdlKFNwZWVkQXZnX01waDIpCiAgICApCgojIGV4YW1wbGVzIHdoZXJlIFNwZWVkQXZnX01waDIgPCAzLjI4NDg3NzAKVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAoUm93TnVtX09HID49IDQ4NTMzOCAmIFJvd051bV9PRyA8PSA0ODUzNTgpIHwgIyA0ODUzNDggIC0tICBFeHRyZW1lIHRyYXZlbCB0aW1lLCBSb3V0ZSBDaGFuZ2UKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMzQ2OTUyICYgUm93TnVtX09HIDw9IDM0Njk3MikgfCAjIDM0Njk2MiAgLS0gRXh0cmVtZSB0cmF2ZWwgdGltZSwgUm91dGUgQ2hhbmdlIAogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSA3MDQ5NCAmIFJvd051bV9PRyA8PSA3MDUxNCkgfCAjIDcwNTA0ICAtLSAgRXh0cmVtZSB0cmF2ZWwgdGltZSwgUm91dGUgQ2hhbmdlCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDIwNTE4NDYgJiBSb3dOdW1fT0cgPD0gMjA1MTg2NikgIyAyMDUxODU2ICAtLSAgRXh0cmVtZSB0cmF2ZWwgdGltZSwgUm91dGUgQ2hhbmdlCiAgICAgICAgICAgKQogICAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBTcGVlZEF2Z19NcGgyLgoKTGltaXQgdGhlIGRhdGFzZXQgYmFzZWQgb24gU3BlZWRBdmdfTXBoMi4KYGBge3J9CgpOb091dGxpZXJzU3BlZWQgPC0gZmlsdGVyKE5vT3V0bGllcnNfVHJhdmVsRGlzdE5UaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgIGJldHdlZW4oU3BlZWRBdmdfTXBoMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDQuMDY5MzAwLCAjIDV0aCBwZXJjZW50aWxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA1Ni4wNTY1MSAjOTV0aCBwZXJjZW50aWxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICApCgpucm93KE5vT3V0bGllcnNfVHJhdmVsRGlzdE5UaW1lKSAtIG5yb3coTm9PdXRsaWVyc1NwZWVkKQoKc3VtbWFyeShOb091dGxpZXJzU3BlZWQpCgpgYGAKCgpUcmF2ZWxUaW1lIG5vdyBsb29rcyBsaWtlIGl0IGhhcyBzb21lIG9kZCB2YWx1ZXMgb24gdGhlIGhpZ2ggZW5kLiAgU28gbGV0J3MgbG9vayBhdCB0aG9zZS4KClZpZXcoVHJhdlRpbWVfTm9PdXRfUGN0aWxlcyk6ICBWaXJ0dWFsbHkgYWxsIHRyaXBzIHNob3VsZCB0YWtlIGxlc3MgdGhhbiA1IG1pbnV0ZXMuIChUaGUgOTl0aCBwZXJjZW50aWxlIG9mIG9mIFRyYXZlbFRpbWUgaXMgYXBwcm94aW1hdGVseSA4IG1pbnV0ZXMuKQpgYGB7cn0KClRyYXZUaW1lX05vT3V0X050aWxlIDwtIGFzLmRhdGEuZnJhbWUoTm9PdXRsaWVyc1NwZWVkJFRyYXZlbFRpbWVfSHIpICU+JSAKICBtdXRhdGUoUGN0aWxlID0gbnRpbGUoTm9PdXRsaWVyc1NwZWVkJFRyYXZlbFRpbWVfSHIsIDEwMCksCiAgICAgICAgIE1pblIgPSBtaW5fcmFuayhOb091dGxpZXJzU3BlZWQkVHJhdmVsVGltZV9IciksCiAgICAgICAgIFBjdFIgPSBwZXJjZW50X3JhbmsoTm9PdXRsaWVyc1NwZWVkJFRyYXZlbFRpbWVfSHIpLAogICAgICAgICBQY3RSX1JvdW5kID0gcm91bmQoUGN0UiwgMikKICAgICAgICApCgpjb2xuYW1lcyhUcmF2VGltZV9Ob091dF9OdGlsZSlbMV0gPC0gIlRyYXZlbFRpbWVfSHIiCnN0cihUcmF2VGltZV9Ob091dF9OdGlsZSkKClRyYXZUaW1lX05vT3V0X050aWxlX1Jvd3MgPC0gbnJvdyhUcmF2VGltZV9Ob091dF9OdGlsZSkKClZpZXcodGFpbChUcmF2VGltZV9Ob091dF9OdGlsZSwgNTAwKSkKCgpUcmF2VGltZV9Ob091dF9QY3RpbGVzIDwtIGdyb3VwX2J5KFRyYXZUaW1lX05vT3V0X050aWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBjdFJfUm91bmQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHN1bW1hcmlzZSgKICAgIE1pblRyYXZUaW1lSHJBdFBjdGlsZSA9IG1pbihUcmF2ZWxUaW1lX0hyKSwKICAgIENudHNBdFBjdGlsZSA9IG4oKSwKICAgIFBjdHNBdFBjdGlsZSA9IENudHNBdFBjdGlsZSAvIFRyYXZUaW1lX05vT3V0X050aWxlX1Jvd3MKICApICU+JSAKICBtdXRhdGUoQ3VtU3VtUEF0UCA9IGN1bXN1bShQY3RzQXRQY3RpbGUpLAogICAgICAgICBNaW5UcmF2VGltZVNlY0F0UGN0aWxlID0gTWluVHJhdlRpbWVIckF0UGN0aWxlICogKDYwICogNjApCiAgICAgICAgKQoKVmlldyhUcmF2VGltZV9Ob091dF9QY3RpbGVzKQoKYGBgCgoKSW52ZXN0aWdhdGluZyBvZGQgVHJhdmVsVGltZV9TZWMgdmFsdWVzLgoKVHJpcHMgbG9uZ2VyIHRoYW4gfjggbWludXRlcy4KYGBge3J9CgpWaWV3KGZpbHRlcihOb091dGxpZXJzU3BlZWQsCiAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjID4gNDkxICMgbWluIGF0IHRoZSAxMDB0aCBwZXJjZW50aWxlCiAgICAgICAgICAgKSAlPiUgCiAgICAgICBhcnJhbmdlKGRlc2MoVHJhdmVsVGltZV9TZWMpCiAgICAgICAgICAgICAgKQogICAgKQoKIyBleGFtcGxlcyBvZiBUcmF2ZWxUaW1lX1NlYyB2YWx1ZXMgdGhhdCBhcmUgbGFyZ2VzdC4KVmlldyhmaWx0ZXIoTm9PdXRsaWVyc1NwZWVkLAogICAgICAgICAgICAoUm93TnVtX09HID49IDIwNzE3NTkgJiBSb3dOdW1fT0cgPD0gMjA3MTc3OSkgfCAjIDIwNzE3NjkgIC0tICByZXN1bHRzIGZyb20gYSByb3V0ZSBjaGFuZ2UsIGFuZCBhIDNocisgd2FpdCBiZWZvcmUgdGhlIG5ldyByb3V0ZSBzdGFydHMKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMTQ3MzY4NiAmIFJvd051bV9PRyA8PSAxNDczNzA2KSB8ICMgMTQ3MzY5NiAgLS0gIHJlc3VsdHMgZnJvbSBhIHJvdXRlIGNoYW5nZSwgYW5kIGEgM2hyIHdhaXQgYmVmb3JlIHRoZSBuZXcgcm91dGUgc3RhcnRzCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDEyMjI4MjIgJiBSb3dOdW1fT0cgPD0gMTIyMjg0MikgfCAjIDEyMjI4MzIgIC0tICByZXN1bHRzIGZyb20gYSByb3V0ZSBjaGFuZ2UsIGFuZCBhIDNociB3YWl0IGJlZm9yZSB0aGUgbmV3IHJvdXRlIHN0YXJ0cwogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAzMDQ2MDg5ICYgUm93TnVtX09HIDw9IDMwNDYxMDkpICMgMzA0NjA5OSAgLS0gIHJlc3VsdHMgZnJvbSBhIHJvdXRlIGNoYW5nZSwgYW5kIGEgM2hyIHdhaXQgYmVmb3JlIHRoZSBuZXcgcm91dGUgc3RhcnRzCiAgICAgICAgICAgKQogICAgKQoKCiMgZXhhbXBsZXMgb2YgVHJhdmVsVGltZV9TZWMgdmFsdWVzIHRoYXQgYXJlIHRoZSBzbWFsbGVzdCBvZiB0aGUgbGFyZ2UuClZpZXcoZmlsdGVyKE5vT3V0bGllcnNTcGVlZCwKICAgICAgICAgICAgKFJvd051bV9PRyA+PSAzMDQ0Njg5ICYgUm93TnVtX09HIDw9IDMwNDQ3MDkpIHwgIyAzMDQ0Njk5ICAtLSAgcmVzdWx0cyBmcm9tIGEgcm91dGUgY2hhbmdlCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDMwMjIzNTggJiBSb3dOdW1fT0cgPD0gMzAyMjM3OCkgfCAjIDMwMjIzNjggIC0tICByZXN1bHRzIGZyb20gYSByb3V0ZSBjaGFuZ2UKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMjk5MzAxNiAmIFJvd051bV9PRyA8PSAyOTkzMDM2KSB8ICMgMjk5MzAyNiAgLS0gIHJlc3VsdHMgZnJvbSBhIHByZXZpb3VzIHJvdXRlIGNoYW5nZSAoY2hhbmdlIG9jY3VycmVkIGluIGRlbGV0ZWQgcm93KQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAyNjgzNzAzICYgUm93TnVtX09HIDw9IDI2ODM3MjMpICMgMjY4MzcxMyAgLS0gIHJlc3VsdHMgZnJvbSBhIHByZXZpb3VzIHJvdXRlIGNoYW5nZSAoY2hhbmdlIG9jY3VycmVkIGluIGRlbGV0ZWQgcm93KQogICAgICAgICAgICkKICAgICkKCmBgYAoKCkxldCdzIGxvb2sgYXQgdGhlIFRyYXZlbFRpbWVfU2VjIHZhbHVlcyBhbmQgcm91dGUgY2hhbmdlcyAoRGlyQ2hhbmdlMikuCgpUaGUgOTl0aCBwZXJjZW50aWxlIG9mIFRyYXZlbFRpbWVfU2VjIGZvciBib3RoLCBhbGwgdHJpcHMsIGFuZCBqdXN0IHRob3NlIHRyaXBzIE5PVCBpbnZvbHZpbmcgcm91dGUgY2hhbmdlcyAoRGlyQ2hhbmdlMiA9ICJTYW1lIiksIGlzIGFwcHJveGltYXRlbHkgNW1pbiAoMzAwIHNlYykuCgpOb3RhIEJlbmU6ICBUaGUgcGVyY2VudGlsZSBjYWxjdWxhdGlvbiBoZXJlIGlzIGRlZmluZWQgc2xpZ2h0bHkgZGlmZmVyZW50IHRoYW4gaW4gbW9zdCBvZiB0aGUgYWJvdmUgYW5hbHlzZXMgKHdoaWNoIGdldCB0aGUgbG93ZXN0IHZhbHVlIGluIHRoZSBiaW4gY3JlYXRlZCBieSAxMDAgbnRpbGVzKS4KYGBge3J9CgpzdW1tYXJ5KHNlbGVjdChOb091dGxpZXJzU3BlZWQsCiAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjCiAgICAgICAgICAgICAgKQogICAgICAgKQoKc3VtbWFyeShzZWxlY3QoZmlsdGVyKE5vT3V0bGllcnNTcGVlZCwKICAgICAgICAgICAgICAgICAgICAgIERpckNoYW5nZTIgPT0gIlNhbWUiCiAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjCiAgICAgICAgICAgICAgKQogICAgICAgKQoKc3VtbWFyeShzZWxlY3QoZmlsdGVyKE5vT3V0bGllcnNTcGVlZCwKICAgICAgICAgICAgICAgICAgICAgIERpckNoYW5nZTIgPT0gIkNoYW5nZSIKICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgVHJhdmVsVGltZV9TZWMKICAgICAgICAgICAgICApCiAgICAgICApCgoKVHJhdlRpbWVTZWNfUXRpbGVzX2RmIDwtIGRhdGEuZnJhbWUoUGN0VmFsdWUgPSBzZXEoMCwgMTAwLCAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQWxsID0gc2VxKDEsIDEwMSwgMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNhbWUgPSBzZXEoMSwgMTAxLCAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ2hhbmdlID0gc2VxKDEsIDEwMSwgMSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgpUcmF2VGltZVNlY19RdGlsZXNfZGZbICwgMl0gPC0gcXVhbnRpbGUoc2VsZWN0KE5vT3V0bGllcnNTcGVlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2JzID0gc2VxKDAsIDEsIDAuMDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKClRyYXZUaW1lU2VjX1F0aWxlc19kZlsgLCAzXSA8LSBxdWFudGlsZShzZWxlY3QoZmlsdGVyKE5vT3V0bGllcnNTcGVlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGlyQ2hhbmdlMiA9PSAiU2FtZSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvYnMgPSBzZXEoMCwgMSwgMC4wMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKVHJhdlRpbWVTZWNfUXRpbGVzX2RmWyAsIDRdIDwtIHF1YW50aWxlKHNlbGVjdChmaWx0ZXIoTm9PdXRsaWVyc1NwZWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEaXJDaGFuZ2UyID09ICJDaGFuZ2UiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2JzID0gc2VxKDAsIDEsIDAuMDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKClZpZXcoVHJhdlRpbWVTZWNfUXRpbGVzX2RmKQoKYGBgCgoKTGltaXQgdGhlIGRhdGFzZXQgbm93IGJhc2VkIG9uIFRyYXZlbFRpbWVfU2VjLgpgYGB7cn0KClVwcGVyTGltaXRUcmF2VGltZSA8LSBmaWx0ZXIoTm9PdXRsaWVyc1NwZWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjIDw9IDQ5MSAjIG1pbiBhdCB0aGUgMTAwdGggcGVyY2VudGlsZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCm5yb3coTm9PdXRsaWVyc1NwZWVkKSAtIG5yb3coVXBwZXJMaW1pdFRyYXZUaW1lKQoKc3RyKFVwcGVyTGltaXRUcmF2VGltZSkKCnN1bW1hcnkoVXBwZXJMaW1pdFRyYXZUaW1lKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBEd2VsbF9UaW1lMiAoaG93IGxvbmcgdGhlIGJ1cyBpcyBhdCBhIHN0b3ApLgoKRGlmZmVyZW5jZXMgYmV0d2VlbiBEd2VsbF9UaW1lIChieSBXTUFUQSkgYW5kIER3ZWxsX1RpbWUyIChieSBtZSkgYXBwZWFyIHRvIGJlIGR1ZSB0byBzd2l0Y2hlcyBpbiBSb3V0ZUFsdC4gV01BVEEgY2FsY3VsYXRlcyBEd2VsbF9UaW1lIGJ5IGFuIHVua25vd24gcHJvY2Vzcy4gVGhlIFdNQVRBIGNhbGN1bGF0aW9uIGlzIGVxdWFsIHRvIG15IGNhbGN1bGF0aW9uLCBleGNlcHQgZm9yIHRoZSByZWNvcmRzIGltbWVkYWl0ZWx5IGJlZm9yZSBhbmQgYWZ0ZXIgYSBSb3V0ZUFsdCBzd2l0Y2ggKERpckNoYW5nZTIpLgpgYGB7cn0KClZpZXcoZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgIER3ZWxsX1RpbWUgIT0gRHdlbGxfVGltZTIKICAgICAgICAgICApCiAgICApCgoKIyBFeGFtcGxlcyB3aGVyZSB0aGUgRHdlbGxfVGltZSBhbmQgRHdlbGxfVGltZTIgYXJlIGRpZmZlcmVudApWaWV3KGZpbHRlcihBbGxEYXlzX05ld09yZGVyLAogICAgICAgICAgICAoIChSb3dOdW1fT0cgPj0gNjUgJiBSb3dOdW1fT0cgPD0gODUpIHwgIyA3NQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAxNjIgJiBSb3dOdW1fT0cgPD0gMTkyKSB8ICMgMTcyCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDQzMTk1MiAmIFJvd051bV9PRyA8PSA0MzE5NzIpIHwgIyA0MzE5NjIKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gNDM0NTk1ICYgUm93TnVtX09HIDw9IDQzNDYxNSkgIyA0MzQ2MDUgIC0tICB0aGlzIHJlY29yZCBpcyBOT1QgYSByb3V0ZSBzd2l0Y2gsIGJ1dCBkb2VzIGhhcyBhIFNlcXVlbmNlIHN3aXRjaCAoTWU6IHNob3VsZCB0aGVyZSByZWFsbHkgYmUgYSByb3V0ZSBzd2l0Y2ggaGVyZT8pCiAgICAgICAgICAgICkKICAgICAgICAgICApCiAgICApCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIER3ZWxsX1RpbWUyIChob3cgbG9uZyB0aGUgYnVzIGlzIGF0IGEgc3RvcCkuCgpGaXJzdCwgY3JlYXRlIHNvbWUgInJhbmsiIHN0YXRzLgpWaWV3KERUMl9QY3RpbGVzKTogOTUlIG9mIER3ZWxsX1RpbWUycyBhcmUgPD0gMjMgc2Vjb25kcy4uLmJ1dCBzb21lIHdlaXJkIChlLmcuLCBuZWFybHkgMiBob3VyIER3ZWxsX1RpbWUycyBleGlzdCkuCmBgYHtyfQoKRHdlbGxUaW1lMl9OdGlsZSA8LSBhcy5kYXRhLmZyYW1lKEFsbERheXNfTmV3T3JkZXIkRHdlbGxfVGltZTIpICU+JSAKICBtdXRhdGUoUGN0aWxlID0gbnRpbGUoQWxsRGF5c19OZXdPcmRlciREd2VsbF9UaW1lMiwgMTAwKSwKICAgICAgICAgTWluUiA9IG1pbl9yYW5rKEFsbERheXNfTmV3T3JkZXIkRHdlbGxfVGltZTIpLAogICAgICAgICBQY3RSID0gcGVyY2VudF9yYW5rKEFsbERheXNfTmV3T3JkZXIkRHdlbGxfVGltZTIpLAogICAgICAgICBQY3RSX1JvdW5kID0gcm91bmQoUGN0UiwgMikKICAgICAgICApIAoKY29sbmFtZXMoRHdlbGxUaW1lMl9OdGlsZSlbMV0gPC0gIkR3ZWxsX1RpbWUyIgpzdHIoRHdlbGxUaW1lMl9OdGlsZSkKCkR3ZWxsVGltZTJfTnRpbGVfUm93cyA8LSBucm93KER3ZWxsVGltZTJfTnRpbGUpCgpWaWV3KHRhaWwoRHdlbGxUaW1lMl9OdGlsZSwgNTAwKSkKCgpEd2VsbFRpbWUyX1BjdGlsZXMgPC0gZ3JvdXBfYnkoRHdlbGxUaW1lMl9OdGlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBjdFJfUm91bmQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKAogICAgTWluRHdlbGxBdFBjdGlsZSA9IG1pbihEd2VsbF9UaW1lMiksCiAgICBDbnRzQXRQY3RpbGUgPSBuKCksCiAgICBQY3RzQXRQY3RpbGUgPSBDbnRzQXRQY3RpbGUgLyBEd2VsbFRpbWUyX050aWxlX1Jvd3MKICApICU+JSAKICBtdXRhdGUoQ3VtU3VtUEF0UCA9IGN1bXN1bShQY3RzQXRQY3RpbGUpCiAgICAgICAgKQoKVmlldyhEd2VsbFRpbWUyX1BjdGlsZXMpCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIER3ZWxsX1RpbWUyIChob3cgbG9uZyB0aGUgYnVzIGlzIGF0IGEgc3RvcCkuCgpIaXN0b2dyYW0gb2YgRHdlbGxfVGltZTIuCmBgYHtyfQoKRHdlbGxUaW1lMl9IaXN0RGVuIDwtIGdncGxvdChBbGxEYXlzX05ld09yZGVyLCBhZXMoeCA9IER3ZWxsX1RpbWUyLCB5ID0gLi5kZW5zaXR5Li4pKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxLCBmaWxsID0gImxpZ2h0Ymx1ZSIsIGNvbG91ciA9ICJncmV5NjAiLCBzaXplID0gMC4yKSArCiAgZ2VvbV9saW5lKHN0YXQgPSAiZGVuc2l0eSIsIGNvbG91ciA9ICJyZWQiKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDEsIDI1KSwgeWxpbSA9IGMoMCwgMC4wNSkKICAgICAgICAgICAgICAgICApICsKICB4bGFiKCJUaW1lIGEgQnVzIFN0YXlzIGF0IGEgU3RvcCAoc2VjKSIpICsgCiAgeWxhYigiRGVuc2l0eSIpICsgCiAgIyAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKyAKICBnZ3RpdGxlKGV4cHJlc3Npb24oYXRvcCgiVmFyaWF0aW9uIGluIEhvdyBMb25nIGEgQnVzIFN0YXlzIGF0IGEgU3RvcCIKICAgICAgICAgICAgICAgICAgICAgICAgICAjICxhdG9wKGl0YWxpYygieHh4eHgiKSwiIikKICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICkKCkR3ZWxsVGltZTJfSGlzdERlbgoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBEd2VsbF9UaW1lMiAoaG93IGxvbmcgdGhlIGJ1cyBpcyBhdCBhIHN0b3ApLgoKTG9va2luZyBhdCBzb21lIHdlaXJkbHkgbG9uZyBEd2VsbF9UaW1lMiB2YWx1ZXMuCmBgYHtyfQoKVmlldyhhcnJhbmdlKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgICBkZXNjKER3ZWxsX1RpbWUyKQogICAgICAgICAgICApCiAgICApCgoKIyBleGFtcGxlcyBvZiBleHRyZW1lbHkgbGFyZ2UgRHdlbGxfVGltZTJzClZpZXcoZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMjkyNjY5ICYgUm93TnVtX09HIDw9IDI5MjY4OSkgfCAjIDI5MjY3OQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSA1MzEwNTcgJiBSb3dOdW1fT0cgPD0gNTMxMDc3KSB8ICMgNTMxMDY3CiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDEzODg2MjcgJiBSb3dOdW1fT0cgPD0gMTM4ODY0NykgfCAjIDEzODg2MzcKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMTY0NTcxMSAmIFJvd051bV9PRyA8PSAxNjQ1NzMxKSAjIDE2NDU3MjEKICAgICAgICAgICApCiAgICApCgoKVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgRHdlbGxfVGltZTIgPT0gMAogICAgICAgICAgICkKICAgICkKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgRGVsdGFfVGltZSAoaG93IGVhcmx5IG9yIGxhdGUgdGhlIGJ1cyBpcykuCgpWaWV3KERUMl9QY3RpbGVzKTogOTQlIG9mIERlbHRhX1RpbWUgdmFsdWVzIGFyZSBiZXR3ZWVuIC0yMzYgc2Vjb25kcyBhbmQgMSwyNTkgc2Vjb25kcy4gUm91Z2hseSA2NiUgb2YgcmVjb3JkcyBhcmUgd2l0aGluIDUgbWluIGxhdGUgYW5kIDUgbWluIGVhcmx5Li4uYnV0IHNvbWUgd2VpcmQgKGUuZy4sIGFsbW9zdCA1MCBtaW51dGUgbGF0ZSBvciA0MCBtaW51dGUgZWFybHkpIERlbHRhX1RpbWVzIGV4aXN0LgoKTm90ZSB0aGF0IERlbHRhX1RpbWUgaXMgdGhlIGRpZmZlcmVuY2UgZnJvbSB0aGUgc2NoZWR1bGVkIGJ1cyBhcnJpdmFsLiBTbyBpZiB0d28gYnVzZXMgYXJlIHNjaGVkdWxlZCB0byBhcnJpdmUgYXQgYSBkZXN0aW5hdGlvbiBhdCAxMDowMHBtIGFuZCAxMDoyMHBtLCBhbmQgaWYgdGhlIDEwOjIwcG0gYnVzIGhhcyBhIERlbHRhX1RpbWUgb2YgNSBtaW51dGVzLCB0aGVyZSBhcmUgMjUgbWludXRlcyBiZXR3ZWVuIGJ1cyBhcnJpdmFscyBhdCB0aGUgc3RvcC4KCkFsc28gbm90ZSB0aGF0IGJhc2VkIG9uIGEgY29tbWVudCBhdCBodHRwczovL3BsYW5pdG1ldHJvLmNvbS8yMDE2LzExLzE2L2RhdGEtZG93bmxvYWQtbWV0cm9idXMtdmVoaWNsZS1sb2NhdGlvbi1kYXRhLywgdGhlIERlbHRhX1RpbWUgdmFsdWVzIGRvbid0IGFwcGVhciB0byBjb2luY2lkZSB3aXRoIHB1Ymxpc2hlZCBidXMgc2NoZWR1bGVzIChlLmcuLCB0aGUgWDIgZGVwYXJ0aW5nIGV2ZXJ5IDggbWludXRlcyBkdXJpbmcgcGVhayBob3VycykuCmBgYHtyfQoKRGVsdFRpbWVfTnRpbGUgPC0gYXMuZGF0YS5mcmFtZShBbGxEYXlzX05ld09yZGVyJERlbHRhX1RpbWUpICU+JSAKICBtdXRhdGUoUGN0aWxlID0gbnRpbGUoQWxsRGF5c19OZXdPcmRlciREZWx0YV9UaW1lLCAxMDApLAogICAgICAgICBNaW5SID0gbWluX3JhbmsoQWxsRGF5c19OZXdPcmRlciREZWx0YV9UaW1lKSwKICAgICAgICAgUGN0UiA9IHBlcmNlbnRfcmFuayhBbGxEYXlzX05ld09yZGVyJERlbHRhX1RpbWUpLAogICAgICAgICBQY3RSX1JvdW5kID0gcm91bmQoUGN0UiwgMikKICAgICAgICApIAoKY29sbmFtZXMoRGVsdFRpbWVfTnRpbGUpWzFdIDwtICJEZWx0YV9UaW1lIgpzdHIoRGVsdFRpbWVfTnRpbGUpCgpEZWx0VGltZV9OdGlsZV9Sb3dzIDwtIG5yb3coRGVsdFRpbWVfTnRpbGUpCgpWaWV3KHRhaWwoRGVsdFRpbWVfTnRpbGUsIDUwMCkpCgoKRGVsdFRpbWVfUGN0aWxlcyA8LSBncm91cF9ieShEZWx0VGltZV9OdGlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQY3RSX1JvdW5kCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzdW1tYXJpc2UoCiAgICBNaW5EZWx0VGltZUF0UGN0aWxlID0gbWluKERlbHRhX1RpbWUpLAogICAgQ250c0F0UGN0aWxlID0gbigpLAogICAgUGN0c0F0UGN0aWxlID0gQ250c0F0UGN0aWxlIC8gRGVsdFRpbWVfTnRpbGVfUm93cwogICkgJT4lIAogIG11dGF0ZShDdW1TdW1QQXRQID0gY3Vtc3VtKFBjdHNBdFBjdGlsZSkKICAgICAgICApCgpWaWV3KERlbHRUaW1lX1BjdGlsZXMpCkRlbHRUaW1lX1BjdGlsZXMKCiMgfjY2JSBvZiByb3dzIGFyZSBiZXR3ZWVuIDUgbWluIGxhdGUgYW5kIDUgbWluIGVhcmx5Cm5yb3coZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgIERlbHRhX1RpbWUgPj0gLTMwMCAmCiAgICAgICAgICAgICAgRGVsdGFfVGltZSA8PSAzMDAKICAgICAgICAgICApCiAgICApIC8gbnJvdyhBbGxEYXlzX05ld09yZGVyKQoKCiMgZXhhbXBsZXMgb2Ygd2VpcmQgbGFyZ2UgRGVsdGFfVGltZXMKVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgRGVsdGFfVGltZSA8IC00MjAyIHwKICAgICAgICAgICAgICBEZWx0YV9UaW1lID4gMTcwNQogICAgICAgICAgICkgJT4lIAogICAgICAgYXJyYW5nZShkZXNjKERlbHRhX1RpbWUpCiAgICAgICAgICAgICAgKQogICAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBEZWx0YV9UaW1lIChob3cgZWFybHkgb3IgbGF0ZSB0aGUgYnVzIGlzKS4KCkRlbHRhX1RpbWUgaGlzdG9ncmFtLgpgYGB7cn0KCkRlbHRUaW1lX0hpc3REZW4gPC0gZ2dwbG90KEFsbERheXNfTmV3T3JkZXIsIGFlcyh4ID0gKERlbHRhX1RpbWUgLyA2MCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gLi5kZW5zaXR5Li4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gKDUvNjApLCBmaWxsID0gImxpZ2h0Ymx1ZSIsIGNvbG91ciA9ICJncmV5NjAiLCBzaXplID0gMC4yKSArCiAgZ2VvbV9saW5lKHN0YXQgPSAiZGVuc2l0eSIsIGNvbG91ciA9ICJyZWQiKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKC01LCA1KSkgKwogIHhsYWIoIkJ1cyBMYXRlbmVzcyAobWluKSIpICsgCiAgeWxhYigiRGVuc2l0eSIpICsgCiAgIyAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKyAKICBnZ3RpdGxlKGV4cHJlc3Npb24oYXRvcCgiVmFyaWF0aW9uIGluIEhvdyBFYXJseS9MYXRlIGEgQnVzIElzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBhdG9wKGl0YWxpYygiKHBvc2l0aXZlIHZhbHVlcyBhcmUgbGF0ZSBhcnJpdmFscykiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICkKCkRlbHRUaW1lX0hpc3REZW4KCmBgYAoKCkludmVzdGlnYXRpb24gb2YgRGVsdGFfVGltZSAoaG93IGVhcmx5IG9yIGxhdGUgdGhlIGJ1cyBpcykuCgpEZWx0YV9UaW1lIGJveHBsb3QuCmBgYHtyfQoKIyBDb3VudF9WYWx1ZXMgaXMgbmVlZGVkIHRvIGRpc3BsYXkgdGhlIG1lZGlhbnMgb24gdGhlIGJveCBwbG90cwpDb3VudF9WYWx1ZXMgPC0gZGRwbHkoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgICAgICAgICAgIC4oRXZlbnRfVGltZV9Ickdyb3VwKSwKICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZSwKICAgICAgICAgICAgICAgICAgICAgIFZhbHVlX0NvdW50cyA9IG1lZGlhbihEZWx0YV9UaW1lIC8gNjAsIG5hLnJtID0gVFJVRSkKICAgICAgICAgICAgICAgICAgICAgKQoKRGVsdFRpbWVfQm94UGxvdCA8LSBnZ3Bsb3QoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGVsdGFfVGltZSAvIDYwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICApICsgCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuY29sb3VyPSJyZWQiLCBub3RjaD1UUlVFKSArIAogICMgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKC0zMDAsIDEyMDApKSArCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKC01LCAyMCkpICsKICBnZW9tX3RleHQoZGF0YSA9IENvdW50X1ZhbHVlcywKICAgICAgICAgICAgYWVzKHkgPSBWYWx1ZV9Db3VudHMsCiAgICAgICAgICAgICAgICBsYWJlbCA9IGZvcm1hdChyb3VuZChWYWx1ZV9Db3VudHMsIGRpZ2l0cyA9IDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnNtYWxsID0gMQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICksCiAgICAgICAgICAgIHNpemUgPSAzLAogICAgICAgICAgICB2anVzdCA9IC0wLjUKICAgICAgICAgICApICsKICB4bGFiKCJIb3VyIEdyb3VwIikgKyAKICB5bGFiKCJCdXMgTGF0ZW5lc3MgKG1pbnV0ZXMpIikgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSkpICsgCiAgI3RoZW1lKGxlZ2VuZC5wb3NpdGlvbj0icmlnaHQiLCBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICBnZ3RpdGxlKGV4cHJlc3Npb24oYXRvcCgiSG93IEVhcmx5L0xhdGUgaXMgdGhlIEJ1cyAoYnkgSG91ciBHcm91cCkiLAogICAgICAgICAgICAgICAgICAgICAgICAgIGF0b3AoaXRhbGljKCIocG9zaXRpdmUgdmFsdWVzIGFyZSBsYXRlIGFycml2YWxzKSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgKQoKRGVsdFRpbWVfQm94UGxvdAoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBEZWx0YV9UaW1lIChob3cgZWFybHkgb3IgbGF0ZSB0aGUgYnVzIGlzKS4KCkV4cGxvcmluZyAiZXh0cmVtZSIgRGVsdGFfVGltZXMuICBGaXJzdCBsZXQncyBnZXQgc29tZSAicmFuayIgc3RhdHMuCmBgYHtyfQoKVmlldyhEZWx0VGltZV9QY3RpbGVzKQpEZWx0VGltZV9QY3RpbGVzCgoKRGVsdFRpbWVBYnNfTnRpbGUgPC0gYXMuZGF0YS5mcmFtZShhYnMoQWxsRGF5c19OZXdPcmRlciREZWx0YV9UaW1lKSkgJT4lIAogIG11dGF0ZShQY3RpbGUgPSBudGlsZShhYnMoQWxsRGF5c19OZXdPcmRlciREZWx0YV9UaW1lKSwgMTAwKSwKICAgICAgICAgTWluUiA9IG1pbl9yYW5rKGFicyhBbGxEYXlzX05ld09yZGVyJERlbHRhX1RpbWUpKSwKICAgICAgICAgUGN0UiA9IHBlcmNlbnRfcmFuayhhYnMoQWxsRGF5c19OZXdPcmRlciREZWx0YV9UaW1lKSksCiAgICAgICAgIFBjdFJfUm91bmQgPSByb3VuZChQY3RSLCAyKQogICAgICAgICkgCgpjb2xuYW1lcyhEZWx0VGltZUFic19OdGlsZSlbMV0gPC0gIkRlbHRhX1RpbWVfQWJzIgpzdHIoRGVsdFRpbWVBYnNfTnRpbGUpCgpEZWx0VGltZUFic19OdGlsZV9Sb3dzIDwtIG5yb3coRGVsdFRpbWVBYnNfTnRpbGUpCgpWaWV3KHRhaWwoRGVsdFRpbWVBYnNfTnRpbGUsIDUwMCkpCgoKRGVsdFRpbWVBYnNfUGN0aWxlcyA8LSBncm91cF9ieShEZWx0VGltZUFic19OdGlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQY3RSX1JvdW5kCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzdW1tYXJpc2UoCiAgICBNaW5EZWx0VGltZUF0UGN0aWxlID0gbWluKERlbHRhX1RpbWVfQWJzKSwKICAgIENudHNBdFBjdGlsZSA9IG4oKSwKICAgIFBjdHNBdFBjdGlsZSA9IENudHNBdFBjdGlsZSAvIERlbHRUaW1lX050aWxlX1Jvd3MKICApICU+JSAKICBtdXRhdGUoQ3VtU3VtUEF0UCA9IGN1bXN1bShQY3RzQXRQY3RpbGUpCiAgICAgICAgKQoKVmlldyhEZWx0VGltZUFic19QY3RpbGVzKQpEZWx0VGltZUFic19QY3RpbGVzCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIERlbHRhX1RpbWUgKGhvdyBlYXJseSBvciBsYXRlIHRoZSBidXMgaXMpLgoKRXhwbG9yaW5nICJleHRyZW1lIiBEZWx0YV9UaW1lcy4gIFRoZW4gbGV0J3MgY2FsY3VsYXRlIHRoZSBwZXJjZW50YWdlIG9mIGJ1c2VzIHRoYXQgYXJlIDEwIG1pbnV0ZXMgKG9yIG1vcmUpIGxhdGUvZWFybHkuCmBgYHtyfQoKSHJHcm91cF9EZWx0YVRpbWVfQWxsIDwtIGdyb3VwX2J5KEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0hyR3JvdXAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKEV2ZW50QWxsX0NudCA9IG4oKQogICAgICAgICAgICkKCnN0cihIckdyb3VwX0RlbHRhVGltZV9BbGwpClZpZXcoSHJHcm91cF9EZWx0YVRpbWVfQWxsKQoKCkhyR3JvdXBfRGVsdGFUaW1lX0Fib3ZlMTBNaW4gPC0gZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFicyhEZWx0YV9UaW1lKSA+PSA2MDAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBncm91cF9ieShFdmVudF9UaW1lX0hyR3JvdXApICU+JSAKICBzdW1tYXJpc2UoRXZlbnRBYm92ZTEwX0NudCA9IG4oKQogICAgICAgICAgICkKCnN0cihIckdyb3VwX0RlbHRhVGltZV9BYm92ZTEwTWluKQpWaWV3KEhyR3JvdXBfRGVsdGFUaW1lX0Fib3ZlMTBNaW4pCgoKSHJHcm91cF9EZWx0YVRpbWVDb21wYXJlIDwtIGlubmVyX2pvaW4oSHJHcm91cF9EZWx0YVRpbWVfQWJvdmUxME1pbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSHJHcm91cF9EZWx0YVRpbWVfQWxsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IGMoIkV2ZW50X1RpbWVfSHJHcm91cCIgPSAiRXZlbnRfVGltZV9Ickdyb3VwIikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBtdXRhdGUoUGN0RXZlbnRzQWJvdmUxMCA9IEV2ZW50QWJvdmUxMF9DbnQgLyBFdmVudEFsbF9DbnQpCgpWaWV3KEhyR3JvdXBfRGVsdGFUaW1lQ29tcGFyZSkKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgRGVsdGFfVGltZSAoaG93IGVhcmx5IG9yIGxhdGUgdGhlIGJ1cyBpcykuCgpRdWlja2x5IHBsb3QgdGhlc2UgImV4dHJlbWUiIERlbHRhX1RpbWVzLiAKYGBge3J9CgpEZWx0VGltZV9BYm92ZTEwX0NvbHMgPC0gZ2dwbG90KEhyR3JvdXBfRGVsdGFUaW1lQ29tcGFyZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoZmFjdG9yKEV2ZW50X1RpbWVfSHJHcm91cCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBjdEV2ZW50c0Fib3ZlMTAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsKICBnZW9tX2NvbChmaWxsID0gImxpZ2h0Ymx1ZSIsIGNvbG91ciA9ICJncmV5NjAiLCBzaXplID0gMC4yKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGZvcm1hdChyb3VuZChQY3RFdmVudHNBYm92ZTEwLCBkaWdpdHMgPSAyKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zbWFsbCA9IDIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICApLAogICAgICAgICAgICBzaXplID0gMywKICAgICAgICAgICAgbnVkZ2VfeSA9IChIckdyb3VwX0RlbHRhVGltZUNvbXBhcmUkUGN0RXZlbnRzQWJvdmUxMCAqIC0wLjEpCiAgICAgICAgICAgKSArCiAgIyBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoLTUsIDUpKSArCiAgeGxhYigiSG91ciBHcm91cCIpICsgCiAgeWxhYigiUGVyY2VudCBvZiBBbGwgQnVzIEFycml2YWxzIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTQ1KSkgKwogIGdndGl0bGUoZXhwcmVzc2lvbihhdG9wKCJXaGVuIGlzIGEgQnVzIDEwKyBNaW51dGVzIExhdGUvRWFybHkiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIyAsYXRvcChpdGFsaWMoInBvc2l0aXZlIHZhbHVlcyBhcmUgbGF0ZSBhcnJpdmFscyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICMgICAgICAiIgogICAgICAgICAgICAgICAgICAgICAgICAgICMgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICkKCkRlbHRUaW1lX0Fib3ZlMTBfQ29scwoKYGBgCgoKUXVpY2sgaW52ZXN0aWdhdGlvbiBvbiB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gRHdlbGxfVGltZTIgKHRoZSB0aW1lIGEgYnVzIGlzIGF0IGEgc3RvcCkgYW5kIERlbHRhX1RpbWUgKGhvdyBlYXJseS9sYXRlIHRoZSBidXMgaXMpLgoKQ29ycmVsYXRpb24uCmBgYHtyfQoKRHdlbGxURGVsdGFUX0NvcnIgPC0gYXMubWF0cml4KGNvcih4ID0gQWxsRGF5c19OZXdPcmRlciREd2VsbF9UaW1lMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gQWxsRGF5c19OZXdPcmRlciREZWx0YV9UaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVzZSA9ICJwYWlyd2lzZSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCkR3ZWxsVERlbHRhVF9Db3JyCgpgYGAKCgpRdWljayBpbnZlc3RpZ2F0aW9uIG9uIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBEd2VsbF9UaW1lMiAodGhlIHRpbWUgYSBidXMgaXMgYXQgYSBzdG9wKSBhbmQgRGVsdGFfVGltZSAoaG93IGVhcmx5L2xhdGUgdGhlIGJ1cyBpcykuCgpOZXh0LCBsZXQncyBnZXQgYSBzYW1wbGUgb2YgZGF0YSBmb3IgcGxvdHRpbmcuIExldCdzIGRvIHRoaXMgZm9yIHRoZSBmdWxsIGRhdGFzZXQgKEFsbERheXNfTmV3T3JkZXIpLgpgYGB7cn0KCkFsbERheXNfTmV3T3JkZXJfMTBQY3RTYW1wIDwtIHNhbXBsZV9mcmFjKEFsbERheXNfTmV3T3JkZXIsIDAuMSkgJT4lIAogIHNlbGVjdChEZWx0YV9UaW1lLAogICAgICAgICBEd2VsbF9UaW1lMgogICAgICAgICkgJT4lIAogIG11dGF0ZShEYXRhU2V0ID0gIkFsbERhdGEiKQoKc3RyKEFsbERheXNfTmV3T3JkZXJfMTBQY3RTYW1wKQoKYGBgCgoKUXVpY2sgaW52ZXN0aWdhdGlvbiBvbiB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gRHdlbGxfVGltZTIgKHRoZSB0aW1lIGEgYnVzIGlzIGF0IGEgc3RvcCkgYW5kIERlbHRhX1RpbWUgKGhvdyBlYXJseS9sYXRlIHRoZSBidXMgaXMpLgoKTGV0J3MgYWxzbyBnZXQgYSBzYW1wbGUgb2YgZGF0YSBmb3IgcGxvdHRpbmcsIGJ1dCB3aXRoIGEgZGF0c2V0IHRoYXQgcmVtb3ZlcyBvdXRsaWVycy4KYGBge3J9CgpWaWV3KERlbHRUaW1lX1BjdGlsZXMpClZpZXcoRHdlbGxUaW1lMl9QY3RpbGVzKQoKQWxsRGF5c19OZXdPcmRlcl9Ob0V4dHJlbWVzXzEwUGN0U2FtcCA8LSBmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmV0d2VlbihEZWx0YV9UaW1lLCAtNDAyLCAxNzA1KSAmICMgcmVtb3ZlcyBhYm91dCAyJSBvZiBEZWx0YV9UaW1lIHZhbHVlcwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJldHdlZW4oRHdlbGxfVGltZTIsIDEsIDYzKSAgIyByZW1vdmVzIGFib3V0IDIlIG9mIER3ZWxsX1RpbWUyIHZhbHVlcwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHNhbXBsZV9mcmFjKDAuMSkgJT4lIAogIHNlbGVjdChEZWx0YV9UaW1lLAogICAgICAgICBEd2VsbF9UaW1lMgogICAgICAgICkgJT4lIAogIG11dGF0ZShEYXRhU2V0ID0gIk91dGxpZXJzUmVtb3ZlZCIpCgpzdHIoQWxsRGF5c19OZXdPcmRlcl9Ob0V4dHJlbWVzXzEwUGN0U2FtcCkKCmBgYAoKClF1aWNrIGludmVzdGlnYXRpb24gb24gdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIER3ZWxsX1RpbWUyICh0aGUgdGltZSBhIGJ1cyBpcyBhdCBhIHN0b3ApIGFuZCBEZWx0YV9UaW1lIChob3cgZWFybHkvbGF0ZSB0aGUgYnVzIGlzKS4KClBsb3R0aW5nIHRoZSBkYXRhIGZyb20gdGhlIGRhdGFzZXQgdGhhdCBkb2VzIG5vdCByZW1vdmUgb3V0bGllcnMuCmBgYHtyfQoKRHdlbGxURGVsdGFUX1NjYXR0ZXIgPC0gZ2dwbG90KEFsbERheXNfTmV3T3JkZXJfMTBQY3RTYW1wLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKER3ZWxsX1RpbWUyLCBEZWx0YV9UaW1lKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsKICBnZW9tX3BvaW50KHNoYXBlID0gMSwgYWxwaGEgPSAwLjUpICsKICBzY2FsZV9zaGFwZShzb2xpZCA9IEZBTFNFKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgY29sb3VyID0gInJlZCIpICsKICAjIHhsYWIoIlRpbWUgYXQgU3RvcCAoc2VjKSIpICsgCiAgIyB5bGFiKCJMYXRlbmVzcyAoc2VjKSIpICsKICBhbm5vdGF0ZShsYWJlbCA9IGxtX2VxbihkZiA9IEFsbERheXNfTmV3T3JkZXJfMTBQY3RTYW1wLAogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBBbGxEYXlzX05ld09yZGVyXzEwUGN0U2FtcCREZWx0YV9UaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgIHggPSBBbGxEYXlzX05ld09yZGVyXzEwUGN0U2FtcCREd2VsbF9UaW1lMgogICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICB4ID0gMjIwMCwKICAgICAgICAgICB5ID0gNjAwLAogICAgICAgICAgIGdlb20gPSAidGV4dCIsCiAgICAgICAgICAgc2l6ZSA9IDMsCiAgICAgICAgICAgY29sb3VyID0gInJlZCIsCiAgICAgICAgICAgcGFyc2UgPSBUUlVFCiAgICAgICAgICApICsKICBsYWJzKHRpdGxlID0gIkxhdGVuZXNzIHZzIFRpbWUgYXQgU3RvcCIsCiAgICAgICBzdWJ0aXRsZSA9ICIobm8gb3V0bGllcnMgcmVtb3ZlZCkiLAogICAgICAgeCA9ICJUaW1lIGF0IFN0b3AgKHNlYykiLAogICAgICAgeSA9ICJMYXRlbmVzcyAoc2VjKSIKICAgICAgKQogICMgZ2d0aXRsZShleHByZXNzaW9uKGF0b3AoIkxhdGVuZXNzIHZzIFRpbWUgYXQgU3RvcCIKICAjICAgICAgICAgICAgICAgICAgICAgICAgICxhdG9wKGl0YWxpYygiKG5vIG91dGxpZXJzIHJlbW92ZWQpIiksCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiIgogICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICkKICAjICAgICAgICAgICAgICAgICAgICkKICAjICAgICAgICApCiMgKwojICAgZ2VvbV9qaXR0ZXIoKQoKRHdlbGxURGVsdGFUX1NjYXR0ZXIKCmBgYAoKClF1aWNrIGludmVzdGlnYXRpb24gb24gdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIER3ZWxsX1RpbWUyICh0aGUgdGltZSBhIGJ1cyBpcyBhdCBhIHN0b3ApIGFuZCBEZWx0YV9UaW1lIChob3cgZWFybHkvbGF0ZSB0aGUgYnVzIGlzKS4KClBsb3R0aW5nIHRoZSBkYXRhIGZyb20gdGhlIGRhdGFzZXQgdGhhdCBkb2VzIHJlbW92ZSBvdXRsaWVycy4KYGBge3J9CgpEd2VsbFREZWx0YVRfU2NhdHRlcl9Ob0V4dHJlbWVzIDwtIGdncGxvdChBbGxEYXlzX05ld09yZGVyX05vRXh0cmVtZXNfMTBQY3RTYW1wLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoRHdlbGxfVGltZTIsIERlbHRhX1RpbWUpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArCiAgZ2VvbV9wb2ludChzaGFwZSA9IDEsIGFscGhhID0gMC41KSArCiAgc2NhbGVfc2hhcGUoc29saWQgPSBGQUxTRSkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGNvbG91ciA9ICJibHVlIikgKwogICMgeGxhYigiVGltZSBhdCBTdG9wIChzZWMpIikgKyAKICAjIHlsYWIoIkxhdGVuZXNzIChzZWMpIikgKwogIGFubm90YXRlKGxhYmVsID0gbG1fZXFuKGRmID0gQWxsRGF5c19OZXdPcmRlcl9Ob0V4dHJlbWVzXzEwUGN0U2FtcCwKICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gQWxsRGF5c19OZXdPcmRlcl9Ob0V4dHJlbWVzXzEwUGN0U2FtcCREZWx0YV9UaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgIHggPSBBbGxEYXlzX05ld09yZGVyX05vRXh0cmVtZXNfMTBQY3RTYW1wJER3ZWxsX1RpbWUyCiAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgIHggPSA1MCwKICAgICAgICAgICB5ID0gLTQ3NSwKICAgICAgICAgICBnZW9tID0gInRleHQiLAogICAgICAgICAgIHNpemUgPSAzLAogICAgICAgICAgIGNvbG91ciA9ICJibHVlIiwKICAgICAgICAgICBwYXJzZSA9IFRSVUUKICAgICAgICAgICkgKwogIGxhYnModGl0bGUgPSAiTGF0ZW5lc3MgdnMgVGltZSBhdCBTdG9wIiwKICAgICAgIHN1YnRpdGxlID0gIigyJSBvZiBvdXRsaWVycyByZW1vdmVkKSIsCiAgICAgICB4ID0gIlRpbWUgYXQgU3RvcCAoc2VjKSIsCiAgICAgICB5ID0gIkxhdGVuZXNzIChzZWMpIgogICAgICApCiAgIyBnZ3RpdGxlKGV4cHJlc3Npb24oYXRvcCgiTGF0ZW5lc3MgdnMgVGltZSBhdCBTdG9wIgogICMgICAgICAgICAgICAgICAgICAgICAgICAgLGF0b3AoaXRhbGljKCIoMiUgb2Ygb3V0bGllcnMgcmVtb3ZlZCkiKSwKICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIiCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAjICAgICAgICAgICAgICAgICAgICAgICAgKQogICMgICAgICAgICAgICAgICAgICAgKQogICMgICAgICAgICkKIyArCiMgICBnZW9tX2ppdHRlcigpCgpEd2VsbFREZWx0YVRfU2NhdHRlcl9Ob0V4dHJlbWVzCgpgYGAKCgpRdWljayBpbnZlc3RpZ2F0aW9uIG9uIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBEd2VsbF9UaW1lMiAodGhlIHRpbWUgYSBidXMgaXMgYXQgYSBzdG9wKSBhbmQgRGVsdGFfVGltZSAoaG93IGVhcmx5L2xhdGUgdGhlIGJ1cyBpcykuCgpQbG90dGluZyB0aGUgZGF0YSBmcm9tIGJvdGggZGF0YXNldHMgdG9nZXRoZXIuCmBgYHtyfQoKQ29tYmluZWREYXRhIDwtIHJiaW5kKEFsbERheXNfTmV3T3JkZXJfMTBQY3RTYW1wLAogICAgICAgICAgICAgICAgICAgICAgQWxsRGF5c19OZXdPcmRlcl9Ob0V4dHJlbWVzXzEwUGN0U2FtcAogICAgICAgICAgICAgICAgICAgICApCgpDb21iaW5lZERhdGEkRGF0YVNldCA8LSBmYWN0b3IoQ29tYmluZWREYXRhJERhdGFTZXQpCgpzdHIoQ29tYmluZWREYXRhKQoKCkR3ZWxsVERlbHRhVF9TY2F0dGVyX0NvbWJpbmVkIDwtIGdncGxvdChDb21iaW5lZERhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IER3ZWxsX1RpbWUyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBEZWx0YV9UaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IERhdGFTZXQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArCiAgZ2VvbV9wb2ludChzaGFwZSA9IDEsIGFscGhhID0gMC41KSArCiAgc2NhbGVfc2hhcGUoc29saWQgPSBGQUxTRSkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLCA1MDApLCB5bGltID0gYygtMTAwMCwgMjAwMCkKICAgICAgICAgICAgICAgICApICsKICBnZW9tX3Ntb290aChkYXRhID0gZmlsdGVyKENvbWJpbmVkRGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIERhdGFTZXQgPT0gIkFsbERhdGEiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgbWV0aG9kID0gImxtIiwKICAgICAgICAgICAgICBjb2xvdXIgPSAicmVkIgogICAgICAgICAgICAgKSArCiAgZ2VvbV9zbW9vdGgoZGF0YSA9IGZpbHRlcihDb21iaW5lZERhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBEYXRhU2V0ID09ICJPdXRsaWVyc1JlbW92ZWQiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgbWV0aG9kID0gImxtIiwKICAgICAgICAgICAgICBjb2xvdXIgPSAiYmx1ZSIKICAgICAgICAgICAgICkgKwogICMgZmFjZXRfd3JhcCggfiBEYXRhU2V0LCBuY29sID0gMikgKwogIGFubm90YXRlKGxhYmVsID0gbG1fZXFuKGRmID0gQWxsRGF5c19OZXdPcmRlcl8xMFBjdFNhbXAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IEFsbERheXNfTmV3T3JkZXJfMTBQY3RTYW1wJERlbHRhX1RpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IEFsbERheXNfTmV3T3JkZXJfMTBQY3RTYW1wJER3ZWxsX1RpbWUyCiAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgIHggPSAzMDAsCiAgICAgICAgICAgeSA9IC02MDAsCiAgICAgICAgICAgZ2VvbSA9ICJ0ZXh0IiwKICAgICAgICAgICBzaXplID0gMywKICAgICAgICAgICBjb2xvdXIgPSAicmVkIiwKICAgICAgICAgICBwYXJzZSA9IFRSVUUKICAgICAgICAgICkgKwogIGFubm90YXRlKGxhYmVsID0gbG1fZXFuKGRmID0gQWxsRGF5c19OZXdPcmRlcl9Ob0V4dHJlbWVzXzEwUGN0U2FtcCwKICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gQWxsRGF5c19OZXdPcmRlcl9Ob0V4dHJlbWVzXzEwUGN0U2FtcCREZWx0YV9UaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgIHggPSBBbGxEYXlzX05ld09yZGVyX05vRXh0cmVtZXNfMTBQY3RTYW1wJER3ZWxsX1RpbWUyCiAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgIHggPSAzMDAsCiAgICAgICAgICAgeSA9IC04MDAsCiAgICAgICAgICAgZ2VvbSA9ICJ0ZXh0IiwKICAgICAgICAgICBzaXplID0gMywKICAgICAgICAgICBjb2xvdXIgPSAiYmx1ZSIsCiAgICAgICAgICAgcGFyc2UgPSBUUlVFCiAgICAgICAgICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKwogIGxhYnModGl0bGUgPSAiTGF0ZW5lc3MgdnMgVGltZSBhdCBTdG9wIiwKICAgICAgIHggPSAiVGltZSBhdCBTdG9wIChzZWMpIiwKICAgICAgIHkgPSAiTGF0ZW5lc3MgKHNlYykiCiAgICAgICkKICAjIGdndGl0bGUoZXhwcmVzc2lvbihhdG9wKCJMYXRlbmVzcyB2cyBUaW1lIGF0IFN0b3AiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIyAsYXRvcChpdGFsaWMoIjIlIG9mIG91dGxpZXJzIHJlbW92ZWQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAjICAgICAgICIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIyAgICAgICkKICAgICAgICAgIyAgICAgICAgICAgICAgICAgKQogICAgICAgICAjICAgICAgICAgICAgKQogICAgICAgICAjICkKIyArCiMgICBnZW9tX2ppdHRlcigpCgpEd2VsbFREZWx0YVRfU2NhdHRlcl9Db21iaW5lZAoKYGBgCgoKCgoKCgoKCgoKQWRkIGEgbmV3IGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqSW5zZXJ0IENodW5rKiBidXR0b24gb24gdGhlIHRvb2xiYXIgb3IgYnkgcHJlc3NpbmcgKkNtZCtPcHRpb24rSSouCgpXaGVuIHlvdSBzYXZlIHRoZSBub3RlYm9vaywgYW4gSFRNTCBmaWxlIGNvbnRhaW5pbmcgdGhlIGNvZGUgYW5kIG91dHB1dCB3aWxsIGJlIHNhdmVkIGFsb25nc2lkZSBpdCAoY2xpY2sgdGhlICpQcmV2aWV3KiBidXR0b24gb3IgcHJlc3MgKkNtZCtTaGlmdCtLKiB0byBwcmV2aWV3IHRoZSBIVE1MIGZpbGUpLgo=